Realtid API (SignalR)
SignalR ger realtidsuppdateringar för autentisering och signering. Perfekt för webbapplikationer som vill visa QR-koder och statusändringar utan polling.
Säker arkitektur
Sessioner startas via REST API (med API-nyckel). SignalR används endast för att prenumerera på uppdateringar med en engångstoken.
Sessioner startas via REST API (med API-nyckel). SignalR används endast för att prenumerera på uppdateringar med en engångstoken.
Säkerhetsmodell
SignalR-hubben kräver en prenumerationstoken för att förhindra missbruk:
| Steg | Vem | Beskrivning |
|---|---|---|
| 1 | Backend | Anropa REST API med API-nyckel → får sessionId + subscriptionToken |
| 2 | Backend | Skicka sessionId och subscriptionToken till frontend |
| 3 | Frontend | Anslut till SignalR-hubben |
| 4 | Frontend | Anropa Subscribe(sessionId, subscriptionToken) |
| 5 | Server | Validerar token (engångs, 5 min giltig), startar polling, skickar events |
Översikt
SignalR-hubben erbjuder:
- Automatisk QR-kodsuppdatering - Ny QR-kod skickas varje sekund
- Statusändringar i realtid - Inga polling-anrop behövs
- WCAG-stöd - Tidsgränser och möjlighet att förlänga session
- Automatisk orderregenerering - Nya orders var 25:e sekund
Komplett flöde
1. Backend: Starta session via REST API
// Backend (C#) - skyddad av API-nyckel
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Api-Key", "YOUR_API_KEY");
var response = await client.PostAsJsonAsync(
"https://id.tic.io/api/v1/auth/bankid/start",
new { endUserIp = userIp });
var result = await response.Content.ReadFromJsonAsync<AuthStartResponse>();
// Returnera till frontend
return new {
sessionId = result.SessionId,
subscriptionToken = result.SubscriptionToken,
autoStartToken = result.AutoStartToken
};
2. Frontend: Anslut och prenumerera
import * as signalR from '@microsoft/signalr';
// Hämta session från din backend
const { sessionId, subscriptionToken, autoStartToken } = await fetch('/api/start-auth')
.then(r => r.json());
// Anslut till SignalR
const connection = new signalR.HubConnectionBuilder()
.withUrl('https://id.tic.io/hub/auth')
.withAutomaticReconnect()
.build();
// Registrera event handlers INNAN anslutning
connection.on('OnQRCode', qrData => renderQrCode(qrData));
connection.on('OnStatusChanged', data => showStatus(data.message));
connection.on('OnCompleted', data => handleSuccess(data));
connection.on('OnFailed', (code, data) => handleError(data.message));
// Anslut
await connection.start();
// Prenumerera med token (engångs, kan ej återanvändas)
const subscribeResult = await connection.invoke('Subscribe', sessionId, subscriptionToken);
console.log('Prenumererar på session:', subscribeResult.sessionId);
Subscribe
Prenumerera på realtidsuppdateringar för en session.
Metodsignatur
Subscribe(sessionId: Guid, subscriptionToken: string) → SubscribeResponse
Parametrar
| Parameter | Typ | Beskrivning |
|---|---|---|
| sessionId Required | Guid | Session-ID från REST API-svaret |
| subscriptionToken Required | string | Prenumerationstoken från REST API-svaret. Engångs, giltig i 5 minuter. |
Response
{
"sessionId": "550e8400-e29b-41d4-a716-446655440000",
"provider": "bankid",
"status": "pending",
"autoStartToken": "7c40b5c9-a3de-4e...",
"sessionExpiresAt": "2026-01-31T12:05:00Z"
}
Övriga metoder
| Metod | Beskrivning | Returvärde |
|---|---|---|
Cancel() |
Avbryter pågående session | void |
GetQRCode() |
Hämtar aktuell QR-kodsdata (användbart om du missar en uppdatering) | string? |
ExtendSession() |
Förlänger sessionens tidsgräns (WCAG). Kan endast anropas en gång per session. | { success, error?, newExpiresAt? } |
GetStatus() |
Hämtar aktuell sessionsstatus | AuthStatusResponse? |
Events (Server → Klient)
Servern skickar dessa events automatiskt efter Subscribe.
// QR-kod uppdateras var 2:a sekund
connection.on('OnQRCode', (qrData) => {
QRCode.toCanvas(document.getElementById('qr-canvas'), qrData);
});
// Status ändras
connection.on('OnStatusChanged', (data) => {
document.getElementById('status').textContent = data.message;
});
// Autentisering/signering slutförd
connection.on('OnCompleted', (data) => {
console.log('Användare:', data.user.givenName, data.user.surname);
console.log('Personnummer:', data.user.personalNumber);
});
// Misslyckades
connection.on('OnFailed', (hintCode, data) => {
document.getElementById('error').textContent = data.message;
});
// Avbruten
connection.on('OnCancelled', () => {
console.log('Session avbruten');
});
// Timeout-varning (60 sekunder kvar)
connection.on('OnTimeoutWarning', (data) => {
if (data.canExtend) {
showExtendDialog(data.secondsRemaining);
}
});
// Order regenererad (var 25:e sekund)
connection.on('OnOrderRegenerated', (data) => {
console.log(`Order ${data.orderCount}/${data.maxOrders}`);
});
Event-referens
| Event | Data | Beskrivning |
|---|---|---|
OnQRCode |
string qrData |
Ny QR-kodsdata. Uppdateras var 2:a sekund. |
OnStatusChanged |
{ status, hintCode, message, messageEn } |
Statusändring med användarmeddelande. |
OnCompleted |
{ sessionId, status, user, completedAt } |
Session slutförd. user innehåller personnummer, namn. |
OnFailed |
hintCode, { message, messageEn } |
Session misslyckades. |
OnCancelled |
- | Session avbruten av användaren. |
OnTimeoutWarning |
{ secondsRemaining, canExtend } |
60 sekunder kvar (WCAG-krav). |
OnOrderRegenerated |
{ orderCount, maxOrders, sessionExpiresInSeconds } |
Ny BankID-order skapad. |
Mobilt flöde (samma enhet)
För autentisering på samma enhet, använd autoStartToken för att öppna BankID-appen.
// autoStartToken kommer från REST API-svaret (via din backend)
const bankIdUrl = `bankid:///?autostarttoken=${autoStartToken}&redirect=null`;
// Öppna BankID-appen
window.location.href = bankIdUrl;
// Events fortsätter att skickas via SignalR
Felhantering
| Fel | Orsak |
|---|---|
Invalid or expired subscription token |
Token har redan använts eller gått ut (5 min) |
Subscription token does not match session ID |
Token tillhör en annan session |
Session not found |
Sessionen finns inte |
Session is not pending |
Sessionen är redan slutförd eller misslyckad |
try {
await connection.invoke('Subscribe', sessionId, subscriptionToken);
} catch (error) {
if (error.message.includes('expired')) {
// Token har gått ut - starta ny session via backend
const newSession = await fetch('/api/start-auth').then(r => r.json());
await connection.invoke('Subscribe', newSession.sessionId, newSession.subscriptionToken);
}
}
Anslutningshantering
WS
wss://id.tic.io/hub/auth
const connection = new signalR.HubConnectionBuilder()
.withUrl('https://id.tic.io/hub/auth')
.withAutomaticReconnect()
.configureLogging(signalR.LogLevel.Warning)
.build();
connection.onreconnecting(error => {
console.log('Återansluter...', error);
});
connection.onreconnected(connectionId => {
console.log('Återansluten:', connectionId);
// OBS: Du måste prenumerera igen med en NY token
});
connection.onclose(error => {
console.log('Anslutning stängd', error);
});
Vid återanslutning
Prenumerationstoken är engångs. Vid återanslutning måste du hämta en ny token
från din backend och anropa
Subscribe igen.