BankID — Zkrinet-plugin
På Zkrinet: lägg till blocket under Byggblock i panelen — ett klick, din egen kod rörs inte och en ögonblicksbild tas först. Installationsstegen nedan gäller om du kör PocketBase själv, till exempel efter en export.
Logga in med svenskt BankID via en EU-baserad OIDC-mäklare: Idura (f.d. Criipto, Danmark) eller Signicat (Norge). Testat mot PocketBase 0.40.4.
#Ärligt om vad som är vad
Själva inloggningen är PocketBase egen. PocketBase har en inbyggd OpenID Connect-leverantör (platserna oidc, oidc2 och oidc3 under OAuth2). Den sköter inloggningssidan, PKCE, kodutbytet, token och användardata. Pluginet bygger inte en egen inloggning. Det lägger till fyra saker:
- En kontroll som säger exakt vad som saknas och jämför dina inställningar mot mäklarens discovery-dokument.
- Förberedelse av
users: BankID lämnar inte ut någon e-postadress, och utan ändringen kan PocketBase inte skapa kontot (MÄTT:400 email: Cannot be blank). E-post blir valfri för BankID-konton men krävs fortfarande vid vanlig registrering. - Namn: mäklarens
name, eller förnamn + efternamn (given_name+family_name) omnamesaknas. - Personnummer: sparas inte som standard och tas bort ur svaret till webbläsaren (se GDPR nedan). Om du vill kan du slå på en koppling via hash, så att kontot överlever ett byte av mäklare.
#Installera
Kopiera pb_hooks/ och pb_migrations/ in i projektet och starta om. Sätt Application URL under Settings till din riktiga adress (https).
#Kontrollen
GET /api/zk/bankid/kontroll (superuser)
GET /api/zk/bankid/kontroll?discovery=https://…/.well-known/openid-configuration
Svarar med:
aterkopplingsadress: den du klistrar in hos mäklaren (https://dinapp.se/api/oauth2-redirect).leverantorer: per BankID-plats vilken mäklare det är (idura/signicat/okand), vad som saknas, ochforslag, alltså de exakta värdena ur mäklarens discovery-dokument att klistra in i PocketBase (inklusive rättacr_values).varningarom:- Application URL saknas, är http eller pekar på localhost
- OAuth2 är avstängd, eller e-post är obligatorisk igen
- ingen OIDC-leverantör, eller Client ID/secret/URL saknas
- en adress är http
- adresserna stämmer inte med discovery
- issuer saknas eller är fel
acr_valuessaknas, så att mäklaren visar en väljare med alla e-legitimationer i stället för BankID- mäklaren listar inte de scopes PocketBase begär
- ID-token läses utan signaturkontroll
- discovery går inte att läsa
Utan ?discovery= hämtas <första issuer>/.well-known/openid-configuration, eller annars samma sökväg på Auth URL:ens adress. Hos Signicat ligger issuern under en sökväg, så ge då adressen från Signicats Dashboard (Well-Known URL).
#Steg för steg: Idura
- Skapa ett konto hos Idura och en Application i Idura Dashboard. Du får en domän på formen
ditt-namn.idura.broker(testdomän först, produktionsdomänen registreras separat). - Lägg till återkopplingsadressen från kontrollen som callback URL. Slå på OAuth2 Code Flow och skapa en client secret. Kopiera Client ID (realm) och hemligheten.
- I PocketBase: Collections → users → ⚙ → OAuth2 → Add provider → OpenID Connect (platsen
oidc).- Client ID + Client secret från steg 2. Display name:
BankID. - Kör
GET /api/zk/bankid/kontroll?discovery=https://ditt-namn.idura.broker/.well-known/openid-configurationoch klistra inforslag: Auth URL (slutar med?acr_values=urn:grn:authn:se:bankid), Token URL, User info URL.
- Client ID + Client secret från steg 2. Display name:
- Kör kontrollen igen tills
varningarär tom. - Test-BankID: Idura hänvisar till BankID:s testportal (developers.bankid.com → test portal). BankID-appen kan bara vara i test- eller produktionsläge, så använd gärna en separat telefon. Du väljer själv namn och ett giltigt testpersonnummer.
- Produktion: enligt Idura kräver BankID-certifikatet ett EU-momsnummer, och Idura beställer det via Swedbank (8–15 arbetsdagar).
Andra BankID-varianter hos Idura: urn:grn:authn:se:bankid:same-device (samma enhet) och urn:grn:authn:se:bankid:qr (QR).
#Steg för steg: Signicat
- Skapa ett sandbox-konto hos Signicat. Enligt Signicat kan Swedish BankID läggas till i sandbox utan avtal (eID Hub → ID Methods).
- Skapa en OIDC-klient, lägg till återkopplingsadressen som redirect URI och skapa en hemlighet.
- Scopes: PocketBase begär alltid
openid email profileoch det går inte att ändra. Tillåt de tre på klienten (kontrollen varnar om discovery inte listar dem). - I PocketBase: OpenID Connect som ovan. Hämta adresserna med
GET /api/zk/bankid/kontroll?discovery=<Well-Known URL från Signicats Dashboard>. Auth URL ska sluta med?acr_values=idp:sbid. - Test-BankID: samma testportal hos BankID som ovan.
Priser: se respektive leverantör. Vi har inte kunnat källhänvisa några priser.
#Knappen i din app
const { record, meta } = await pb.collection("users").authWithOAuth2({ provider: "oidc" })
BankID-konton har ingen e-post (record.email === ""). Behöver appen mejla användaren måste du fråga efter adressen separat. Om kontot är kopplat till BankID ser användaren själv (MÄTT):
const kopplingar = await pb.collection("_externalAuths").getFullList({ filter: 'provider = "oidc"' })
#Personnummer och GDPR
Standard: personnumret sparas ingenstans. Pluginet tar bort det ur svaret till webbläsaren, eftersom PocketBase annars skickar med hela mäklarsvaret (meta.rawUser), personnummer i klartext inräknat (MÄTT). Testet söker igenom alla databasfiler i pb_data efter personnumret, byte för byte. Det som sparas är mäklarens sub (i _externalAuths) och namnet.
sub är en pseudonym per mäklare: hos Idura är den enligt dokumentationen unik per användare och Idura-tenant. Det betyder: byter du mäklare (eller tenant) får varje användare ett nytt sub och därmed ett nytt, tomt konto. Vi skriver det rakt ut eftersom det är en inlåsning.
Valfritt: koppling via hash. Sätt ZK_BANKID_PNR_NYCKEL (minst 32 slumpade tecken) så sparas HMAC-SHA256(personnummer, nyckel) i det dolda fältet users.bankid_pnr_hmac. Loggar samma person in med ett nytt sub kopplas inloggningen till samma konto (testat: byte på samma plats och byte mellan platser). Konsekvenser, ärligt:
- Hashen är fortfarande en personuppgift (pseudonymiserad, inte anonym). Den hör till din registerförteckning och ska tas med vid radering.
- Det finns bara några hundra miljoner möjliga personnummer. Läcker nyckeln kan hashen räknas tillbaka till personnumret på kort tid. Skydda nyckeln som ett lösenord. Den ligger bara i miljön, aldrig i databasen.
- Enligt dataskyddslagen (2018:218) 3 kap. 10 § får personnummer behandlas utan samtycke bara när det är klart motiverat. Att kunna byta leverantör utan att användarna tappar sina konton kan vara ett sådant skäl, men det är din bedömning.
- Bara de platser som står i
ZK_BANKID_LEVERANTORER(standardoidc) kan koppla. En annan OIDC-tjänst påoidc2, där användaren kanske kan sätta egna claims, kan aldrig ta över ett BankID-konto (testat). - Hos Signicat kommer personnumret i claimen
nin, och den kräver scopenin, som PocketBase inte begär. Med Signicat blir kopplingen alltså troligen tom (inte mätt). Hos Idura kommerssnutan extra scope enligt dokumentationen.
Mäklaren behandlar personuppgifter åt dig. Teckna personuppgiftsbiträdesavtal med den.
#Säkerhet
- Använd User info URL (rekommenderas). Då hämtar PocketBase användardata direkt från mäklaren över TLS. Använder du i stället ID-token: sätt
jwksURLochissuersunder extra, så kontrollerar PocketBase signatur, utfärdare och mottagare (MÄTT mot en lokal mäklare: fel nyckel och fel utfärdare nekas). Utan dem läses ID-token okontrollerad, och kontrollen varnar. - Kontrollen kräver superuser och visar aldrig hemligheter (testat).
- Användare kan inte själva sätta
bankid_pnr_hmac(testat). - E-post krävs fortfarande vid all registrering som inte är en OAuth2-inloggning, även via
/api/batch(testat).
#Begränsningar
- PocketBase begär alltid scope
openid email profile(källkodentools/auth/oidc.goi 0.40.4) och har inget fält för egna scopes.acr_valuesfungerar som en del av Auth URL. PocketBase lägger till sina parametrar med&(MÄTT). - Namnet sätts när kontot skapas (PocketBase mappar bara då). Ändrar personen namn uppdateras det inte.
- Ett befintligt lösenordskonto kopplas inte automatiskt till BankID (det finns ingen e-post att matcha på). PocketBase kopplar BankID till kontot om användaren är inloggad när
authWithOAuth2anropas (enligt PocketBase källkod, testat här). - Även superusers måste ange e-post när de skapar användare i adminpanelen. Bara OAuth2-inloggningen slipper.
- Kontrollen hämtar discovery-dokumentet över nätet när den körs.
- Adresser till
127.0.0.1/localhostger ingen https-varning (lokal test-mäklare). Application URL på localhost varnar fortfarande. - Nedmigreringen gör e-post obligatorisk igen och går därför inte igenom så länge det finns BankID-konton utan e-post.
#Det här har vi INTE kunnat mäta
- Riktiga Idura och Signicat: TLS, deras JWKS och nyckelbyten, att
acr_valuesger BankID direkt, hur Signicat reagerar påemail-scopet, och att claim-namnen (ssn,nin,given_name,family_name) ser ut så i verkligheten. Allt det kommer ur deras dokumentation (Idura: docs.idura.app → Swedish BankID, Signicat: developer.signicat.com → Swedish BankID). - Andra signaturalgoritmer för ID-token än RS256.
#Miljövariabler
| Variabel | Standard | Vad |
|---|---|---|
ZK_BANKID_LEVERANTORER | oidc | Vilka OIDC-platser som är BankID (t.ex. oidc,oidc2 under ett mäklarbyte). |
ZK_BANKID_PNR_NYCKEL | (av) | Minst 32 tecken. Slår på koppling via personnummer-hash. |
#Testa
./bank.py bankid
19 tester mot en låtsas-mäklare på 127.0.0.1 (discovery, token, userinfo, JWKS). Inget når internet. Testerna täcker migreringen, e-postkravet (även via batch), kontrollens varningar, inloggning utan e-post, namn, personnumret (sökt byte för byte i pb_data och i svaret), mäklarbyte, att en annan OIDC-plats inte kan ta över ett konto, det dolda fältet och PocketBase egen id_token-kontroll. Säkerhetstesterna och varningarna är verifierade med mutanter: 14 av 14 brutna skydd fångades. ID-token-testet signerar med RS256 och kräver Python-paketet cryptography på testmaskinen. Pluginet självt har inga beroenden.