Udforsk moderne frontend credential management. Lær at bruge Credential Management API, WebAuthn, Passkeys og FedCM til at bygge sikre, brugervenlige login-oplevelser.
Frontend Credential Management: Et dybdegående kig på API'er til adgangskoder og identitet
I det konstant udviklende landskab af webudvikling forbliver login-formularen en fundamental, men ofte frustrerende, brugerinteraktion. I årtier har den simple kombination af brugernavn og adgangskode været portvagten til vores digitale liv. Denne traditionelle tilgang er dog fyldt med udfordringer: adgangskodetræthed, sikkerhedssårbarheder fra svage eller genbrugte legitimationsoplysninger og en klodset brugeroplevelse, der kan føre til høje afvisningsprocenter. Som udviklere navigerer vi konstant i den hårfine balance mellem robust sikkerhed og en friktionsfri brugerrejse.
Heldigvis har webplatformen udviklet sig markant. Moderne browsere leveres nu med en kraftfuld pakke af API'er, der er designet specifikt til at tackle disse autentificeringsudfordringer direkte. Disse værktøjer, der samlet går under paraplyen Credential Management, giver os mulighed for at skabe tilmeldings- og login-oplevelser, der ikke kun er mere sikre, men også dramatisk enklere for slutbrugeren. Denne artikel er en omfattende guide for frontend-udviklere til, hvordan man udnytter disse API'er – fra det grundlæggende Credential Management API til den adgangskodefri fremtid med WebAuthn og den privatlivsbevarende verden af Federated Credential Management (FedCM).
Den gamle garde: Udfordringer ved traditionel formularbaseret autentificering
Før vi dykker ned i de moderne løsninger, er det afgørende at forstå de problemer, de løser. Den klassiske <form> med inputfelter til e-mail og adgangskode har tjent internettet i årevis, men dens begrænsninger er mere tydelige end nogensinde i en verden med forhøjede sikkerhedstrusler og brugerforventninger.
- Dårlig brugeroplevelse (UX): Brugere skal huske unikke, komplekse adgangskoder til dusinvis af tjenester. Dette fører til, at de glemmer legitimationsoplysninger, hvilket resulterer i frustrerende processer for nulstilling af adgangskoder. På mobile enheder er det endnu mere besværligt at indtaste komplekse adgangskoder.
- Sikkerhedsrisici: For at håndtere kompleksiteten af adgangskoder tyr brugere ofte til usikre praksisser som at bruge simple, let gættelige adgangskoder, genbruge den samme adgangskode på tværs af flere sider eller skrive dem ned. Dette gør dem sårbare over for credential stuffing-angreb, hvor angribere bruger lister over stjålne legitimationsoplysninger til at få uautoriseret adgang til andre tjenester.
- Phishing-sårbarheder: Selv erfarne brugere kan blive narret af sofistikerede phishing-sider, der efterligner legitime login-sider for at stjæle deres legitimationsoplysninger. Traditionelle adgangskoder tilbyder ringe eller ingen beskyttelse mod dette.
- Høj udviklingsomkostning: At bygge sikre autentificeringsflows fra bunden er komplekst. Udviklere skal håndtere hashing og salting af adgangskoder, implementere multifaktor-autentificering (MFA), administrere tokens til nulstilling af adgangskoder og beskytte mod forskellige angreb som brute-forcing og timing-angreb.
Disse udfordringer understreger et klart behov for en bedre metode – et system, hvor browseren og operativsystemet kan fungere som betroede mæglere, der forenkler processen for brugeren og samtidig styrker sikkerheden for applikationen.
Den moderne løsning: Credential Management API'et
Credential Management API'et er hjørnestenen i moderne frontend-autentificering. Det giver en standardiseret, programmatisk grænseflade for websites til at interagere med browserens lager af legitimationsoplysninger. Dette lager kan være browserens indbyggede adgangskodeadministrator eller endda en tilsluttet boks på operativsystemniveau. I stedet for udelukkende at stole på heuristikker for automatisk udfyldning af HTML-formularer, giver dette API udviklere mulighed for direkte at anmode om, oprette og gemme brugerlegitimationer.
API'et er tilgængeligt via navigator.credentials-objektet i JavaScript og drejer sig om tre centrale metoder: get(), create() og store().
Væsentlige fordele ved Credential Management API'et
- Et-tryks-login: For tilbagevendende brugere giver API'et mulighed for en næsten øjeblikkelig login-oplevelse. Browseren kan bede brugeren om at vælge en gemt konto, og med et enkelt tryk eller klik leveres legitimationsoplysningerne til websitet.
- Strømlinet tilmelding: Under registrering hjælper API'et ved automatisk at udfylde kendte oplysninger og, ved vellykket tilmelding, prompter det problemfrit brugeren til at gemme deres nye legitimationsoplysninger.
- Understøttelse af flere legitimationstyper: Dette er måske dens mest kraftfulde funktion. API'et er designet til at være udvideligt og understøtter ikke kun traditionelle adgangskoder (
PasswordCredential), men også fødererede identiteter (FederatedCredential) og public key-legitimationsoplysninger, der bruges af WebAuthn (PublicKeyCredential). - Forbedret sikkerhed: Ved at mægle interaktionen hjælper browseren med at mindske sikkerhedsrisici. For eksempel sikrer den, at legitimationsoplysninger kun er tilgængelige for den oprindelse (domæne), de blev gemt for, hvilket giver en iboende beskyttelse mod mange phishing-angreb.
Praktisk implementering: Logge brugere ind med `navigator.credentials.get()`
get()-metoden bruges til at hente en brugers legitimationsoplysninger til login. Du kan specificere, hvilke typer legitimationsoplysninger din applikation understøtter.
Forestil dig, at en bruger lander på din login-side. I stedet for at de skal skrive noget, kan du straks tjekke, om de har en gemt legitimation.
async function handleSignIn() {
try {
// Tjek om API'et er tilgængeligt
if (!navigator.credentials) {
console.log('Credential Management API understøttes ikke.');
// Fallback til at vise den traditionelle formular
return;
}
const cred = await navigator.credentials.get({
// Vi anmoder om en adgangskodebaseret legitimation
password: true,
// Du kan også anmode om andre typer, som vi vil dække senere
});
if (cred) {
// En legitimation blev valgt af brugeren
console.log('Legitimation modtaget:', cred);
// Send nu legitimationen til din server for verifikation
await serverLogin(cred.id, cred.password);
} else {
// Brugeren lukkede prompten eller har ingen gemte legitimationer
console.log('Ingen legitimation valgt.');
}
} catch (err) {
console.error('Fejl ved hentning af legitimation:', err);
// Håndter fejl, f.eks. ved at vise den traditionelle formular
}
}
async function serverLogin(username, password) {
// Dette er en mock-funktion. I en rigtig app ville du sende
// dette til din backend via en POST-anmodning.
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
});
if (response.ok) {
window.location.href = '/dashboard'; // Omdiriger ved succes
} else {
// Håndter loginfejl
console.error('Login mislykkedes på serveren.');
}
}
I dette eksempel udløser kaldet navigator.credentials.get({ password: true }), at browseren viser en native brugergrænseflade (ofte en kontovælger), der lister alle gemte legitimationsoplysninger for det aktuelle domæne. Hvis brugeren vælger en, løses promiset med et PasswordCredential-objekt, der indeholder id (brugernavn) og password. Din applikation kan derefter sende disse oplysninger til serveren for at fuldføre autentificeringsprocessen.
Praktisk implementering: Gemme legitimationsoplysninger med `navigator.credentials.store()`
Efter en bruger har tilmeldt sig eller logget ind med en traditionel formular (måske som et fallback), bør du tilbyde at gemme deres legitimationsoplysninger til fremtidig brug. store()-metoden gør dette problemfrit.
async function handleSuccessfulSignUp(username, password) {
try {
// Opret et nyt PasswordCredential-objekt
const newCredential = new PasswordCredential({
id: username,
password: password,
name: 'Brugerens visningsnavn' // Valgfrit: til kontovælgeren
});
// Gem legitimationen
await navigator.credentials.store(newCredential);
console.log('Legitimation gemt med succes!');
// Fortsæt med at omdirigere brugeren eller opdatere UI'et
window.location.href = '/welcome';
} catch (err) {
console.error('Fejl ved lagring af legitimation:', err);
}
}
Når denne kode kører, vil browseren præsentere en diskret prompt, der spørger brugeren, om de vil gemme adgangskoden. Dette er en meget bedre brugeroplevelse end at stole på browserens undertiden uforudsigelige heuristikker til at opdage et vellykket login og tilbyde at gemme adgangskoden.
Den næste grænse: Adgangskodefri autentificering med WebAuthn og Passkeys
Mens Credential Management API'et dramatisk forbedrer oplevelsen omkring adgangskoder, er det ultimative mål for mange at eliminere adgangskoder helt. Det er her, Web Authentication API (WebAuthn) kommer ind i billedet. WebAuthn er en W3C-standard, der muliggør adgangskodefri, phishing-resistent autentificering ved hjælp af public key-kryptografi.
Du har måske hørt udtrykket Passkeys for nylig. Passkeys er den brugervenlige implementering af standarden bag WebAuthn. En passkey er en digital legitimation, der gemmes på en brugers enhed (som en telefon, computer eller hardware-sikkerhedsnøgle). Den bruges til at logge ind på websites og apps uden en adgangskode. De synkroniseres ofte på tværs af en brugers enheder via skytjenester (som iCloud Keychain eller Google Password Manager), hvilket gør dem utroligt bekvemme.
Hvorfor WebAuthn er en game-changer for sikkerheden
- Phishing-resistent: En passkey er kryptografisk bundet til det websites oprindelse, hvor den blev oprettet. Det betyder, at en passkey oprettet for
my-bank.comikke kan bruges til at logge ind på en phishing-side sommy-bank-login.com. Browseren vil simpelthen ikke tillade det. - Ingen delte hemmeligheder: Med WebAuthn genererer brugerens enhed et public/private key-par. Den private nøgle forlader aldrig brugerens sikre enhed (autentifikatoren). Kun den offentlige nøgle sendes til serveren. Selv hvis din servers database bliver kompromitteret, vil angribere ikke finde nogen adgangskoder at stjæle.
- Stærk multifaktor-autentificering: En passkey kombinerer i sagens natur, hvad brugeren har (enheden med den private nøgle) og hvad brugeren er (deres fingeraftryk/ansigt) eller ved (deres enheds-PIN). Dette opfylder ofte MFA-krav i et enkelt, simpelt trin.
WebAuthn-flowet via Credential Management API'et
WebAuthn styres også gennem navigator.credentials-objektet ved hjælp af PublicKeyCredential-typen. Processen involverer to hovedstadier: registrering og autentificering.
1. Registrering (Oprettelse af en Passkey)
Dette er en forenklet oversigt. Den faktiske implementering kræver omhyggelig håndtering af kryptografiske udfordringer på serversiden.
- Klienten anmoder om at registrere: Brugeren angiver, at de vil oprette en passkey.
- Serveren sender en udfordring: Din server genererer en unik, tilfældig udfordring og nogle konfigurationsmuligheder (et
publicKeyCreationOptions-objekt). - Klienten kalder `navigator.credentials.create()`: Din frontend-kode sender indstillingerne fra serveren til denne metode.
- Brugeren godkender: Browseren/operativsystemet beder brugeren om at oprette en passkey ved hjælp af enhedens autentifikator (f.eks. Face ID, Windows Hello eller en fingeraftryksscanning). Autentifikatoren opretter et nyt public/private key-par.
- Klienten sender den offentlige nøgle til serveren: Den resulterende legitimation, som inkluderer den nye offentlige nøgle og en signeret attestering, sendes tilbage til din server til verifikation og lagring.
const creationOptions = await fetch('/api/webauthn/register-options').then(r => r.json());
// Vigtigt: Den servergenererede udfordring skal afkodes fra Base64URL til en BufferSource
creationOptions.challenge = bufferDecode(creationOptions.challenge);
creationOptions.user.id = bufferDecode(creationations.user.id);
const credential = await navigator.credentials.create({ publicKey: creationOptions });
2. Autentificering (Login med en Passkey)
- Klienten anmoder om at logge ind: Brugeren ønsker at logge ind med sin passkey.
- Serveren sender en udfordring: Din server genererer en ny tilfældig udfordring og sender den til klienten (inden i et
publicKeyRequestOptions-objekt). - Klienten kalder `navigator.credentials.get()`: Denne gang bruger du
publicKey-indstillingen. - Brugeren godkender: Brugeren autentificerer sig med sin enhed. Enhedens autentifikator bruger den gemte private nøgle til at underskrive udfordringen fra serveren.
- Klienten sender assertion til serveren: Den underskrevne udfordring (kaldet en assertion) sendes tilbage til din server. Serveren verificerer signaturen ved hjælp af den gemte offentlige nøgle. Hvis den er gyldig, bliver brugeren logget ind.
const requestOptions = await fetch('/api/webauthn/login-options').then(r => r.json());
requestOptions.challenge = bufferDecode(requestOptions.challenge);
const credential = await navigator.credentials.get({ publicKey: requestOptions });
Bemærk: Det rå WebAuthn API indebærer betydelig kompleksitet, især omkring kodning/afkodning af data (som ArrayBuffers og Base64URL). Det anbefales kraftigt at bruge et gennemtestet bibliotek som SimpleWebAuthn eller en tjenesteudbyder til at håndtere de lav-niveau detaljer på både klient og server.
Privatlivsfokuseret login: Federated Credential Management (FedCM)
I årevis har "Log ind med Google/Facebook/GitHub" været en populær måde at reducere friktionen ved tilmelding. Denne model kaldes Federated Identity. Historisk set har den i høj grad været afhængig af mekanismer som omdirigeringer, pop-ups og tredjepartscookies til at spore login-status på tværs af sider. I takt med at browsere bevæger sig mod at udfase tredjepartscookies for at forbedre brugernes privatliv, er disse traditionelle flows i fare for at gå i stykker.
Federated Credential Management API (FedCM) er et nyt forslag, der er designet til at fortsætte med at understøtte fødererede identitets-use cases på en privatlivsbevarende måde uden at være afhængig af tredjepartscookies.
Hovedmål for FedCM
- Bevare fødererede logins: Give brugerne mulighed for fortsat at bruge deres foretrukne identitetsudbydere (IdP'er) til let at logge ind på Relying Parties (RP'er, dit website).
- Forbedre privatlivets fred: Forhindre IdP'er i passivt at spore brugere på tværs af internettet uden deres udtrykkelige samtykke.
- Forbedre brugeroplevelse og sikkerhed: Tilvejebringe en browser-medieret, standardiseret brugergrænseflade for fødererede logins, der giver brugerne mere gennemsigtighed og kontrol over, hvilke data der deles. Dette hjælper også med at forhindre UI-baserede phishing-angreb.
Sådan virker FedCM (overordnet)
Med FedCM orkestrerer browseren selv login-flowet og fungerer som en betroet mellemmand mellem dit site (RP) og identitetsudbyderen (IdP).
- RP anmoder om en legitimation: Dit website kalder
navigator.credentials.get(), denne gang med specificering af enfederated-udbyder. - Browseren henter manifester: Browseren laver sandboxed-anmodninger til en
/.well-known/web-identity-fil på IdP'ens domæne. Denne fil fortæller browseren, hvor den kan finde de nødvendige endepunkter til at hente kontolister og udstede tokens. - Browseren viser en kontovælger: Hvis brugeren er logget ind hos IdP'en, viser browseren sin egen native brugergrænseflade (f.eks. en dropdown i øverste højre hjørne af skærmen), der viser brugerens tilgængelige konti. RP'ens sideindhold bliver aldrig skjult.
- Brugeren giver samtykke: Brugeren vælger en konto og giver samtykke til at logge ind.
- Browseren henter et token: Browseren foretager en sidste anmodning til IdP'ens token-endepunkt for at få et ID-token.
- RP modtager tokenet: Promiset fra
get()løses og returnerer etFederatedCredential-objekt, der indeholder tokenet. Dit website sender dette token til din backend, som skal validere det med IdP'en, før der oprettes en session for brugeren.
async function handleFedCMLogin() {
try {
const cred = await navigator.credentials.get({
federated: {
providers: ['https://accounts.google.com', 'https://facebook.com'], // Eksempel på IdP'er
// Browseren vil lede efter en well-known manifest-fil på disse domæner
}
});
// Hvis det lykkes, indeholder legitimationsobjektet et token
if (cred) {
console.log('Modtaget token:', cred.token);
// Send tokenet til din server for validering og login
await serverLoginWithToken(cred.token, cred.provider);
}
} catch (err) {
console.error('FedCM-fejl:', err);
}
}
FedCM er stadig et relativt nyt API, og browserunderstøttelsen er under udvikling, men det repræsenterer den fremtidige retning for tredjepartslogins på internettet.
En samlet strategi: Progressiv forbedring af autentificering
Med tre forskellige typer legitimationsoplysninger tilgængelige, hvordan skal du så strukturere din frontend-kode? Den bedste tilgang er progressiv forbedring. Du bør sigte mod at levere den mest moderne, sikre oplevelse som muligt, mens du yndefuldt falder tilbage på ældre metoder, når det er nødvendigt.
Credential Management API'et er designet til dette. Du kan anmode om alle understøttede legitimationstyper i et enkelt get()-kald, og browseren vil prioritere og præsentere den bedste mulighed for brugeren.
Det anbefalede autentificeringsflow
- Prioritér Passkeys (hvis tilgængelige): For den mest sikre og problemfri oplevelse, tjek først om brugeren har en passkey. Du kan bruge
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()til funktionsdetektering for betinget at vise en "Log ind med Passkey"-knap. - Brug et samlet `get()`-kald: Foretag et enkelt kald til
navigator.credentials.get(), der inkluderer muligheder forpublicKey,passwordog _potentielt_federated. Browseren er smart omkring dette; for eksempel vil den ikke vise en adgangskodeprompt, hvis en passkey er tilgængelig og foretrækkes. - Håndter den returnerede legitimation: Tjek typen af det returnerede legitimationsobjekt ved hjælp af
instanceofog behandl det i overensstemmelse hermed. - Yndefuldt fallback: Hvis brugeren annullerer prompten, eller API-kaldet mislykkes af en eller anden grund (f.eks. i en ikke-understøttet browser), så og kun så skal du vise den fulde, traditionelle brugernavn/adgangskode-formular.
Eksempel: Et samlet `get()`-kald
async function unifiedSignIn() {
try {
// Bemærk: Disse `publicKey`- og `federated`-indstillinger ville komme fra din server
const publicKeyOptions = await fetch('/api/webauthn/login-options').then(r => r.json());
// ... (logik for buffer-afkodning her) ...
const cred = await navigator.credentials.get({
password: true,
publicKey: publicKeyOptions,
federated: {
providers: ['https://idp.example.com']
},
// 'optional' forhindrer en fejl, hvis brugeren ingen legitimationsoplysninger har
mediation: 'optional'
});
if (!cred) {
console.log('Brugeren annullerede eller ingen legitimationsoplysninger. Viser formular.');
showTraditionalLoginForm();
return;
}
// Håndter legitimationen baseret på dens type
if (cred instanceof PasswordCredential) {
console.log('Håndterer password-legitimation...');
await serverLogin(cred.id, cred.password);
} else if (cred instanceof PublicKeyCredential) {
console.log('Håndterer PublicKeyCredential (Passkey)...');
await serverLoginWithPasskey(cred);
} else if (cred instanceof FederatedCredential) {
console.log('Håndterer FederatedCredential (FedCM)...');
await serverLoginWithToken(cred.token, cred.provider);
}
} catch (err) {
console.error('Fejl ved samlet login:', err);
showTraditionalLoginForm(); // Fallback ved enhver fejl
}
}
Globale overvejelser og bedste praksis
Når du implementerer disse moderne autentificeringsflows for et globalt publikum, skal du huske følgende:
- Browserunderstøttelse: Tjek altid browserkompatibilitet for hvert API på sider som caniuse.com. Sørg for robuste fallbacks for brugere på ældre browsere for at sikre, at ingen bliver låst ude.
- Validering på serversiden er ikke til forhandling: Frontend er et upålideligt miljø. Alle legitimationsoplysninger, tokens og assertions modtaget fra klienten skal valideres grundigt på serveren, før en session oprettes. Disse API'er forbedrer frontend UX; de erstatter ikke backend-sikkerhed.
- Brugeruddannelse: Koncepter som passkeys er nye for mange brugere. Brug klart, enkelt sprog. Overvej at tilføje tooltips eller links til korte forklaringer (f.eks. "Hvad er en passkey?") for at guide brugerne gennem processen og opbygge tillid.
- Internationalisering (i18n): Mens de browser-native brugergrænseflader typisk lokaliseres af browser-leverandøren, skal enhver brugerdefineret tekst, fejlmeddelelser eller instruktioner, du tilføjer, oversættes korrekt til dine målgrupper.
- Tilgængelighed (a11y): Hvis du bygger brugerdefinerede UI-elementer til at udløse disse flows (som brugerdefinerede knapper), skal du sikre, at de er fuldt tilgængelige med korrekte ARIA-attributter, fokustilstande og understøttelse af tastaturnavigation.
Konklusion: Fremtiden er nu
Æraen med udelukkende at stole på besværlige og usikre adgangskodeformularer er ved at være forbi. Som frontend-udviklere er vi nu udstyret med et kraftfuldt sæt browser-API'er, der giver os mulighed for at bygge autentificeringsoplevelser, der samtidigt er mere sikre, mere private og langt mere brugervenlige.
Ved at omfavne Credential Management API'et som et samlet indgangspunkt kan vi progressivt forbedre vores applikationer. Vi kan tilbyde bekvemmeligheden ved et-tryks adgangskodelogins, den pansersikre sikkerhed fra WebAuthn og passkeys, og den privatlivsfokuserede enkelhed fra FedCM. Rejsen væk fra adgangskoder er en maraton, ikke en sprint, men værktøjerne til at begynde at bygge den fremtid er tilgængelige for os i dag. Ved at vedtage disse moderne standarder kan vi ikke kun glæde vores brugere, men også gøre internettet til et mere sikkert sted for alle.