Hur man autentiserar med Twilio, CIBA och BankID i telefonsamtal
Av Natalia Moskaleva den 22 januari 2026
10 min lästid

I denna guide skapar vi en liten implementation av Twilios Voice API tillsammans med svenskt BankID i telefonsamtal för att demonstrera enkel och säker användarverifiering.
Användningsfallet
Tänk dig att ditt företag driver ett callcenter där det är viktigt att bekräfta uppringarens identitet.
Istället för att förlita sig på föråldrade säkerhetsfrågor som moderns flicknamn, skulle det inte vara bättre att autentisera uppringare i realtid med hjälp av deras smartphone och en särskild app?
Lyckligtvis har det nya flödet CIBA (Client Initiated Backchannel Authentication) i OpenID Connect utformats för just detta ändamål.
Implementeringen
Idura Verify introducerade nyligen stöd för BankID i telefonsamtal (BankID Phone Authentication) genom CIBA. Vi kan alltså använda BankID säkert och pålitligt för att autentisera användare över telefon.
När det gäller att skapa vårt eget "callcenter" som kan hanteras programmatiskt är Twilio den perfekta motsvarigheten. Twilio erbjuder verktyg för att ringa och ta emot telefonsamtal genom lättanvända API:er - precis vad vi behöver för att visa upp vår idé.
Här är arbetsflödet vi ska bygga:
- En användare ringer det angivna "call center"-numret som hanteras av Twilio.
- Under samtalet uppmanas användaren att ange sitt svenska socialförsäkringsnummer (SSN).
- När SSN har angetts får användaren ett meddelande i sin svenska BankID-app där han eller hon ombeds att autentisera sig.
- Vid lyckad autentisering kan "callcentret" tillhandahålla personlig information till användaren.
Du får gärna följa med. När du går igenom guiden kommer du att ringa till det Twilio-hanterade "callcentret" och autentisera dig med BankID-appen.
Låt oss komma igång!
Konton och verktyg som krävs
Här är vad vi behöver för att slutföra denna handledning:
- Node.js: Vi kommer att skapa en Node.js-applikation, så se till att du har Node.js installerat på din maskin. Du kan ladda ner den senaste versionen från den officiella webbplatsen.
- Twilio-konto och telefonnummer: Registrera dig för ett konto hos Twilio. Välj och konfigurera sedan ett Twilio-telefonnummer.
- Ngrok: Ngrok möjliggör tunnling av vår lokala server till Internet, vilket gör att Twilio kan prata med vår applikation. Du kan installera Ngrok från den officiella webbplatsen.
- Idura-konto: Registrera dig för ett gratis utvecklarkonto på Idura och skapa sedan din första domän och applikation.
- Svensk BankID testapplikation och testanvändare: Dessa instruktioner kommer att leda dig i rätt riktning.
När allt är klart kan vi sätta igång med vår implementation.
Ställ in din utvecklingsmiljö
Initiera ett nytt Node.js-projekt
Skapa en ny katalog för ditt projekt och navigera in i den:
mkdir twilio-ciba-auth
cd twilio-ciba-auth
Initiera ett nytt Node.js-projekt:
npm init -y
Installera nödvändiga beroenden
Vi behöver:
- express: för att skapa en server och hantera routing.
- body-parser: för att analysera inkommande förfrågningar.
- axios: för att göra förfrågningar till externa API:er; kommer att användas för interaktioner med Twilio.
Installera paket genom att köra:
npm install express body-parser axios
Implementera BankID i telefonsamtal
Låt oss börja med att skriva koden för att autentisera en användare med BankID i telefonsamtal. Den här koden kommer att trigga BankID-appautentisering för en specifik användare. Vi kommer senare att integrera den här delen för att hantera autentisering under telefonsamtal.
Läs den fullständiga dokumentationen för mer detaljerad information om varje steg.
Skapa en fil med namnet phone_auth.js. Kräv sedan axios-paketet som vi tidigare installerade och lägg till dina Idura-referenser:
const axios = require('axios');
const domain = {YOUR_IDURA_DOMAIN};
const clientId = {CLIENT_ID_OF_YOUR_IDURA_APPLICATION};
const clientSecret = {CLIENT_SECRET_OF_YOUR_IDURA_APPLICATION};
Vi använder clientId och clientSecret för klientautentisering för att hålla det enkelt. Vi rekommenderar att du använder Private Key JWTs när det är möjligt.
Skriv en funktion för att skicka en HTTP POST-begäran till slutpunkten /ciba/bc-authorize på din domän för att initiera autentiseringsprocessen:
const startAuth = async (ssn) => {
try {
const response = await axios.post(
`https://${domain}/ciba/bc-authorize`,
new URLSearchParams({
scope: 'openid',
callInitiator: 'user', // We expect the user to initiate the call
login_hint: `sub:ssn:${ssn}`, // User’s SSN number
acr_values: 'urn:grn:authn:se:bankid', // acr_values for the Swedish BankID
binding_message: "Hello from Twilio, CIBA and the Swedish Phone Auth Tutorial!", // Message displayed in the app
}).toString(),
{
headers: {
Authorization: 'Basic ' + Buffer.from(`${encodeURIComponent(clientId)}:${clientSecret}`).toString('base64'),
'Content-Type': 'application/x-www-form-urlencoded',
},
}
);
return response.data;
} catch (error) {
console.error('Error in auth:', error.message);
throw error;
}
};
Ett lyckat svar kommer att innehålla begärans ID(auth_req_id) och se ut ungefär så här:
{
"auth_req_id" : "3857f8ff-21b9-48ae-a732-a3bd8128a7ae",
"expires_in" : 120
}
Använd sedan auth_req_id för att polla token-slutpunkten(/oauth2/token) för att få ett svar:
const poll = async (auth_req_id) => {
try {
const response = await axios.post(
`https://${domain}/oauth2/token`,
new URLSearchParams({
auth_req_id: auth_req_id,
grant_type: 'urn:openid:params:grant-type:ciba',
}).toString(),
{
headers: {
Authorization: 'Basic ' + Buffer.from(`${encodeURIComponent(clientId)}:${clientSecret}`).toString('base64'),
'Content-Type': 'application/x-www-form-urlencoded',
},
}
);
return response.data;
} catch (error) {
if (error.response && error.response.status === 400 && error.response.data.error === 'authorization_pending') {
// 'authorization_pending' error is expected, ignore it
return null;
}
console.error('Error in poll:', error.message);
throw error;
}
};
Slutligen skapar du en funktion som hanterar hela autentiseringsflödet och pollar efter token var 5:e sekund:
const authenticate = async (ssn) => {
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
try {
const authResponse = await startAuth(ssn);
const auth_req_id = authResponse.auth_req_id;
for (let i = 0; i < 10; i++) {
try {
const response = await poll(auth_req_id);
if (response) {
console.log('Response:', response);
if (response.id_token) {
console.log('ID Token:', response.id_token);
return response;
}
}
} catch (e) {
console.error('Error during polling:', e.message);
}
await delay(5000);
}
throw new Error('Authentication failed.');
} catch (e) {
console.error('Error in authentication flow:', e.message);
}
};
Anropa funktionen med SSN-numret för din testanvändare:
authenticate('196802020575');
Gå sedan till din terminal och kör koden:
node phone_auth.js
Öppna appen Svenskt BankID test på din smartphone. En testanvändare (vars SSN vi just använde) ska få ett meddelande om säkerhetskontroll:

Välj Ja och autentisera dig sedan i appen.
Om en stund kommer du att se autentiseringsresultatet i din terminal:
Response: {
token_type: 'Bearer',
expires_in: '120',
id_token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiVBQzZG….my1R4gXx_S-34KkIgFn3cr',
access_token: '938fbcbf-b4e0-4818-80f3-e8714203980c'
}
ID-token från den lyckade autentiseringen innehåller JWT-krav med personuppgifter om vår testanvändare, precis som förväntat.
Slutligen tar du bort funktionsanropet och exporterar autentiseringsfunktionen:
module.exports = main;
När autentiseringen av uppringaren har konfigurerats ska vi nu införa Twilio i vår applikation.
Konfigurera Twilio
Twilio är en molnbaserad kommunikationsplattform som tillhandahåller API:er och verktyg som gör det möjligt för företag att integrera röst-, meddelande- och videofunktioner i sina applikationer och webbplatser, vilket eliminerar behovet av att bygga och underhålla en komplex kommunikationsinfrastruktur.
I den här handledningen använder vi Twilios Programmable Voice och Node.js SDK.
Grunderna i Twilio
Innan vi dyker in i hantering av samtal, låt oss snabbt täcka några grundläggande begrepp:
Twilios REST API
Twilios REST API låter dig programmatiskt skicka och ta emot samtal och SMS-meddelanden via HTTP-förfrågningar. Det stöder olika programmeringsspråk och bibliotek. Programmable Voice är Twilios API för hantering av telefonsamtal.
Twilio webhooks
Webhooks är en viktig del av Twilios funktionalitet. En webhook är en URL som Twilio skickar HTTP-förfrågningar till när ditt Twilio-telefonnummer tar emot ett samtal. Webhooks är ansvariga för att ta emot och bearbeta dessa förfrågningar.
TwiML-formatet
När Twilio skickar en HTTP-begäran till din applikation förväntar sig Twilio ett svar i TwiML (Twilio Markup Language), ett XML-baserat format som talar om för Twilio hur de ska svara på samtalet.
Skriva Node.js-kod för att svara på inkommande telefonsamtal
För att hantera inkommande telefonsamtal behöver vi vår webbapplikation för att acceptera HTTP-förfrågningar från Twilio.
När ditt Twilio-nummer tar emot ett samtal skickar Twilio en HTTP POST-begäran till din webhook-slutpunkt och förväntar sig instruktioner om hur samtalet ska hanteras. Din webbapplikation kommer att svara med ett TwiML-dokument som innehåller dessa instruktioner (t.ex. att säga lite text, samla in användarinmatning och mer).
Tillbaka i vår app installerar du Twiilos Node.js SDK genom att köra:
npm install twilio
Skapa sedan en fil som heter get-call.js och inkludera de nödvändiga beroendena:
- express för webbservern,
- urlencoded från body-parser för att analysera inkommande förfrågningar
- VoiceResponse från twilio för att generera TwiML-svar
const express = require('express');
const VoiceResponse = require('twilio').twiml.VoiceResponse;
const urlencoded = require('body-parser').urlencoded;
const app = express();
app.use(urlencoded({ extended: false }));
// Handle incoming calls
app.post('/twilio/webhook/init', (req, res) => {
res.type('xml');
const twiml = new VoiceResponse();
twiml.say('Hello from the server!');
// Send the TwiML response
res.send(twiml.toString());
});
// Start the server
app.listen(1337, '127.0.0.1');
console.log('TwiML server running at http://127.0.0.1:1337/');
I det här exemplet lyssnar Express-servern efter inkommande POST-begäranden från Twilio på port 1337 och dirigerar dem till"/twilio/webhook/init". När en begäran tas emot svarar servern med TwiML som instruerar Twilio (med hjälp av Twilios verb<Say>) att säga "Hej från servern!" under samtalet.
Du kan nu köra applikationen i din terminal:
node get-call.js
Om du vill lära dig mer om hur du hanterar inkommande samtal kan du läsa Twilios omfattande handledning och snabbstartsguide för Node.js.
Gör din applikation tillgänglig för Internet
För att Twilio ska kunna skicka HTTP-förfrågningar till din webbapplikation måste applikationen vara tillgänglig via Internet med en publik URL eller IP-adress. Under utvecklingen kan du använda ngrok för att skapa en publik URL för din lokala server.
När ngrok är installerat och vår applikation körs lokalt öppnar du ett nytt terminalfönster och startar ngrok med det här kommandot:
ngrok http 1337
Detta kommer att skapa en publik URL för vår webbapplikation som lyssnar på port 1337:

Kopiera URL:en från resultatet och klistra in den i din webbläsare.
Du bör se din Node.js-applikations "Hej från servern!"-meddelande.
Konfigurera webhook i Twilio-konsolen
Gå till Twilio-konsolen och navigera till ditt telefonnummer. I avsnittet Voice Configuration ändrar du "A call comes in" till "Webhook" och lägger till din publika URL för ngrok (kom ihåg att lägga till URL-sökvägen: t.ex. https://<your_ngrok_subdomain>.ngrok-free.app/twilio/webhook/init i vårt exempel).

Klicka på"spara" och gå tillbaka till terminalen. Kontrollera att både ngrok och din Node.js-app fortfarande är igång.
Nu kan du ringa ditt Twilio-telefonnummer.
Om en stund kommer du att se en HTTP-förfrågan i din ngrok-konsol och höra ett röstmeddelande när samtalet ansluts.
Utmärkt! Din Node.js-uppsättning hanterar nu telefonsamtal.
Uppdatera Node.js-koden för att samla in användarens input
Låt oss lägga till koden för att uppmana en användare att mata in sitt SSN-nummer.
Ersätt den befintliga koden i rutten "/twilio/webhook/init":
app.post('/twilio/webhook/init', (req, res) => {
res.type('xml');
const twiml = new VoiceResponse();
//twiml.say('Hello from the server!');
const gather = twiml.gather({
input: 'dtmf',
action: '/twilio/webhook/authenticate,
finishOnKey: '#',
timeout: 10,
});
gather.say('Please enter your SSN number followed by the pound sign.');
res.send(twiml.toString());
});
Här använder vi Twilios verb <Gather> för att samla in numerisk inmatning från den som ringer. När denna TwiML körs kommer den som ringer att höra uppmaningen att ange sitt SSN-nummer. Twilio kommer sedan att samla in deras inmatning.
Verbet <Gather> är konfigurerat med dessa attribut:
input: dtmf står för Dual-Tone Multi-Frequency tones, och förväntar sig numerisk inmatning från den som ringer.
åtgärd: Anger den slutpunkt som ansvarar för att hantera användarens inmatning. När den som ringer är klar med att mata in siffror (eller tidsgränsen har uppnåtts) skickar Twilio en HTTP-begäran till denna URL, inklusive den som ringer och Twilios standardparametrar för begäran(CallSid, från, till, etc.)
finishOnKey: Gör det möjligt att ställa in en nyckel (i det här fallet #) för att den som ringer ska skicka in sin inmatning. När Twilio upptäcker # slutar den att vänta på ytterligare inmatning och skickar den till åtgärds-URL:en (exklusive #).
timeout: Twilio väntar på att den som ringer ska trycka på en annan siffra i 10 sekunder innan data skickas till åtgärds-URL:en. Standard timeout är 5 sekunder.
Autentisering av uppringaren
Nu är det dags att implementera verifiering av uppringaren.
Vi skapar rutten /twilio/webhook/authenticate, som tar emot den uppringandes SSN-nummer. Denna rutt kommer asynkront att anropa vår tidigare definierade authenticate-funktion och instruera den som ringer att autentisera sig via sin svenska BankID-app.
För enkelhetens skull lagrar vi idToken (autentiseringsresultatet) i en global variabel. I en produktionsmiljö skulle du behålla användarautentiseringsdata i en databas.
Lägg till följande kod i get-call.js:
let idToken;
app.post('/twilio/webhook/authenticate', async (req, res) => {
const userInput = req.body.Digits;
// Format user input so the numbers are spoken individually by Twilio
// https://www.twilio.com/docs/voice/twiml/say#hints-and-advanced-uses
const spokenUserInput = userInput.split('').join(' ');
console.log(Userinput: ', userInput);
const twiml = new VoiceResponse();
// Respond to user while executing CIBA asynchronously
twiml.say(`Your SSN is: ${spokenUserInput}. Please verify it using your bank app`);
// Pause for 10 seconds to keep the call alive
twiml.pause({ length: 10 });
twiml.redirect({ method: 'POST' }, '/twilio/webhook/result);
// Send the TwiML response;
res.type('xml');
res.send(twiml.toString());
// Call CIBA asynchronously
try {
idToken = await authenticate(userInput);
console.log('from idToken await', idToken);
} catch {
console.error('Authentication error', error);
idToken = null;
}
});
Resultat av autentisering
Rutten /twilio/webhook/result är det sista steget i användarverifieringsflödet, där användardata kan nås och användas för att ge personliga svar.
app.post('/twilio/webhook/result', async (req, res) => {
const twiml = new VoiceResponse();
console.log('idToken: ', idToken);
const checkToken = () => {if (idToken) {
twiml.say('Thank you for verifying your identity. Your account balance is $5000. Goodbye!');
} else {
twiml.say('Something went wrong. Please try again later.');
}
res.type('xml');
res.send(twiml.toString());
};
// Set a delay before checking the token
setTimeout(checkToken, 5000); // // 5 seconds delay
});
Efter en lyckad autentisering (dvs. om idToken inte är null) kommer Twilio att informera den som ringer om kontosaldot. Vi använder ett generiskt kontosaldoutdrag för att hålla det enkelt. I verkligheten innehåller data i JWT-token uppringarens personliga information, så att du kan hälsa på dem med namn, leta upp specifika detaljer i din databas med hjälp av en unik identifierare etc.
Testa arbetsflödet
Nu är det dags att sätta vårt arbete på prov. Om allt har gjorts korrekt kan du nu ringa ditt Twilio-nummer och ange SSN för din testanvändare i telefonen.
Twilio kommer att be dig att autentisera dig. Du kan sedan öppna din BankID-app, svara på meddelandet om säkerhetskontroll och gå igenom autentiseringsprocessen.
Twilio bekräftar att du har verifierats och avslutar samtalet.
Tack för att du har följt med! Vi hoppas att du har haft glädje av den här artikeln.
Om du har några frågor, feedback eller användningsfall som du vill diskutera, vänligen kontakta våra experter.
Du kan också fortsätta lära dig mer om BankID i telefonsamtal här.
Dessa relaterade artiklar

Hur man använder Fiddler för att felsöka autentiseringsflöden

Förstå JWT-validering: En praktisk guide med kodexempel

Vad är Passkeys? En introduktion för nybörjare
Sign up for our newsletter
Stay up to date on industry news and insights