PageSourceSearch

https://test-passkeys.com/chunk-45RRC3HR.js

js test-passkeys.com collected 2026-10-02 01:46:10 UTC 20,554 bytes, 168 lines download raw bytes

1import{n as T,r as E}from"./chunk-JGEVEWWZ.js";import{$a as e,Ja as S,U as k,Ua as v,Ub as z,Va as b,Xa as O,Ya as y,Z as f,Za as C,_ as h,_a as d,ab as t,bb as c,c as w,fb as P,jb as _,kb as p,sb as u,tb as x,va as o,vb as n,wb as g,xb as M,yb as I}from"./chunk-JE7BTD77.js";var D=(r,s)=>s.id;function A(r,s){if(r&1){let i=P();e(0,"button",17),_("click",function(){let l=f(i).$implicit,m=p();return h(m.currentView=l.id)}),c(1,"app-icon",18),e(2,"span"),n(3),t()()}if(r&2){let i=s.$implicit,a=p();u("background",a.currentView===i.id?a.selectedPlatformData.gradient:null),x("seg--active",a.currentView===i.id),o(),d("name",i.icon)("size",16),o(2),g(i.label)}}function q(r,s){if(r&1){let i=P();e(0,"button",19),_("click",function(){let l=f(i).$implicit,m=p();return h(m.selectedPlatform=l.id)}),c(1,"app-icon",18),e(2,"div",20),n(3),t()()}if(r&2){let i=s.$implicit,a=p();u("background",a.selectedPlatform===i.id?i.gradient:null),x("platform--active",a.selectedPlatform===i.id),o(),d("name",i.icon)("size",20),o(2),g(i.name)}}function G(r,s){r&1&&(e(0,"div",13)(1,"h3"),n(2,"What are Passkeys?"),t(),e(3,"p",21),n(4," Passkeys are a replacement for passwords. They use public key cryptography to authenticate users without transmitting secrets over the network. "),t(),e(5,"div",22)(6,"p")(7,"strong",23),n(8,"Key Benefit:"),t(),n(9," Passkeys are phishing-resistant, breach-proof, and provide a seamless user experience. "),t()()())}function B(r,s){if(r&1&&(e(0,"div",25)(1,"span",26),c(2,"app-icon",27),t(),e(3,"p",21),n(4),t()()),r&2){let i=s.$implicit;o(2),d("size",12),o(2),g(i)}}function U(r,s){if(r&1&&(e(0,"div",13)(1,"h3"),n(2,"Passkey Registration"),t(),e(3,"p",21),n(4," During registration, the authenticator creates a new key pair. The private key stays on the device while the public key is sent to the server. "),t(),e(5,"div",24),y(6,B,5,2,"div",25,O),t()()),r&2){let i=p();o(6),C(i.registrationSteps)}}function V(r,s){r&1&&(e(0,"div",13)(1,"h3"),n(2,"Passkey Authentication"),t(),e(3,"p",21),n(4," During authentication, the user proves possession of the private key by signing a challenge from the server. "),t(),e(5,"div",28)(6,"p")(7,"strong",29),n(8,"Security:"),t(),n(9," The private key never leaves the device, making passkeys immune to phishing attacks. "),t()()())}function J(r,s){r&1&&(e(0,"div",13)(1,"h3"),n(2,"Conditional Mediation"),t(),e(3,"p",21),n(4," Mediation allows passkeys to appear as autofill suggestions, enabling one-tap sign-in without user interaction. "),t(),e(5,"div",30)(6,"p")(7,"strong",31),n(8,"UX Tip:"),t(),n(9," Conditional UI creates a seamless login experience by presenting passkeys contextually. "),t()()())}function L(r,s){if(r&1){let i=P();e(0,"div",14)(1,"div",32),c(2,"span",33),e(3,"span",34),n(4),t(),e(5,"button",35),_("click",function(){let l=f(i),m=p();return h(m.copyCode(l.code))}),c(6,"app-icon",18),e(7,"span"),n(8),t()()(),e(9,"pre",36)(10,"code"),n(11),t()()()}if(r&2){let i=s,a=p();o(2),u("background",a.selectedPlatformData.gradient),o(2),I("",a.selectedPlatformData.name," \xB7 ",i.language),o(2),d("name",a.copied?"check":"copy")("size",15),o(2),g(a.copied?"Copied":"Copy"),o(3),g(i.code)}}function F(r,s){if(r&1&&(e(0,"div",15),c(1,"span",33),e(2,"p"),n(3," Pick "),e(4,"strong"),n(5,"Register"),t(),n(6,", "),e(7,"strong"),n(8,"Auth"),t(),n(9,", or "),e(10,"strong"),n(11,"Mediation"),t(),n(12),t()()),r&2){let i=p();o(),u("background",i.selectedPlatformData.gradient),o(11),M(" above to see ",i.selectedPlatformData.name," code for that ceremony. ")}}function K(r,s){r&1&&(e(0,"a",16),c(1,"app-icon",37),e(2,"span")(3,"strong"),n(4,"Try it live:"),t(),n(5," run this ceremony in the Playground and tweak every parameter yourself "),t(),c(6,"app-icon",38),t()),r&2&&(o(),d("size",18),o(5),d("size",16))}var R=class r{route=k(T);selectedPlatform="web";currentView="overview";copied=!1;ngOnInit(){this.route.queryParamMap.subscribe(s=>{let i=s.get("platform");this.platforms.some(l=>l.id===i)&&(this.selectedPlatform=i);let a=s.get("view");this.views.some(l=>l.id===a)&&(this.currentView=a)})}platforms=[{id:"ios",name:"iOS",icon:"smartphone",gradient:"linear-gradient(135deg, #3b82f6, #06b6d4)"},{id:"android",name:"Android",icon:"smartphone",gradient:"linear-gradient(135deg, #22c55e, #10b981)"},{id:"web",name:"Web",icon:"globe",gradient:"linear-gradient(135deg, #f97316, #f59e0b)"},{id:"backend",name:"Backend",icon:"server",gradient:"linear-gradient(135deg, #a85
15f7, #ec4899)"}];views=[{id:"overview",label:"Overview",icon:"eye"},{id:"registration",label:"Register",icon:"user-plus"},{id:"authentication",label:"Auth",icon:"passkey"},{id:"mediation",label:"Mediation",icon:"sparkles"}];registrationSteps=["User initiates registration","Server sends challenge","Authenticator creates key pair","Public key sent to server"];samples={registration:{web:{language:"TypeScript",code:`// 1. Get creation options from your server, decode the base64url fields.
2const opts = await api.post('/credential/create/options');
3opts.challenge = base64urlToBuffer(opts.challenge);
4opts.user.id   = base64urlToBuffer(opts.user.id);
5opts.excludeCredentials = opts.excludeCredentials?.map((c) => ({
6  ...c, id: base64urlToBuffer(c.id),
7}));
8
9// 2. Create the passkey - the browser prompts for Face ID / Touch ID / PIN.
10const cred = await navigator.credentials.create({ publicKey: opts });
11
12// 3. Encode the attestation and send it back to verify + store.
13await api.post('/credential/create/verify', {
14  id: cred.id,
15  rawId: bufferToBase64url(cred.rawId),
16  type: cred.type,
17  response: {
18    clientDataJSON:    bufferToBase64url(cred.response.clientDataJSON),
19    attestationObject: bufferToBase64url(cred.response.attestationObject),
20  },
21});`},ios:{language:"Swift",code:`let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(
22    relyingPartyIdentifier: "test-passkeys.com")
23
24// challenge + userID come from your server (base64url-decoded to Data).
25let request = provider.createCredentialRegistrationRequest(
26    challenge: challenge, name: email, userID: userID)
27
28let controller = ASAuthorizationController(authorizationRequests: [request])
29controller.delegate = self
30controller.presentationContextProvider = self
31controller.performRequests()
32
33// In the delegate, POST the attestation to /credential/create/verify.
34// (base64URLEncodedString() is a small Data extension: base64 -> URL-safe.)
35func authorizationController(controller: ASAuthorizationController,
36        didCompleteWithAuthorization auth: ASAuthorization) {
37    let r = auth.credential as! ASAuthorizationPlatformPublicKeyCredentialRegistration
38    send([
39        "rawId": r.credentialID.base64URLEncodedString(),
40        "clientDataJSON": r.rawClientDataJSON.base64URLEncodedString(),
41        "attestationObject": r.rawAttestationObject!.base64URLEncodedString(),
42    ])
43}`},android:{language:"Kotlin",code:`val credentialManager = CredentialManager.create(context)
44
45// requestJson is the standard WebAuthn creation-options JSON from your server.
46val request = CreatePublicKeyCredentialRequest(requestJson = optionsJson)
47
48val result = credentialManager.createCredential(context, request)
49        as CreatePublicKeyCredentialResponse
50
51// POST the attestation JSON to /credential/create/verify.
52sendToServer(result.registrationResponseJson)`},backend:{language:"Go",code:`// Using github.com/go-webauthn/webauthn
53
54// Begin: registration options + a session (challenge) to stash server-side.
55options, session, err := web.BeginRegistration(user)
56// -> send "options" to the client; keep "session".
57
58// Finish: verify the client's attestation.
59credential, err := web.FinishRegistration(user, session, r)
60// -> persist "credential" (public key, id, sign count) against the user.`}},authentication:{web:{language:"TypeScript",code:`// 1. Get assertion options (challenge + allowCredentials) from your server.
61const opts = await api.post('/auth/login/credential/options', { email });
62opts.challenge = base64urlToBuffer(opts.challenge);
63opts.allowCredentials = opts.allowCredentials?.map((c) => ({
64  ...c, id: base64urlToBuffer(c.id),
65}));
66
67// 2. Sign the challenge - the user picks a passkey and verifies.
68const assertion = await navigator.credentials.get({ publicKey: opts });
69
70// 3. Send the signed assertion back for verification.
71await api.post('/auth/login/credential/verify', {
72  id: assertion.id,
73  rawId: bufferToBase64url(assertion.rawId),
74  type: assertion.type,
75  response: {
76    clientDataJSON:    bufferToBase64url(assertion.response.clientDataJSON),
77    authenticatorData: bufferToBase64url(assertion.response.authenticatorData),
78    signature:         bufferToBase64url(assertion.response.signature),
79    // userHandle is null unless the credential is discoverable - guard it.
80    userHandle: assertion.response.userHandle
81      ? bufferToBase64url(assertion.response.userHandle) : null,
82  },
83});`},ios:{language:"Swift",code:`let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(
84    relyingPartyIdentifier: "test-passkeys.com")
85
86// challenge comes from your server, base64url-decoded to Data.
87let request = provider.createCredentialAssertionRequest(challenge: challenge)
88
89let controller = ASAuthorizationController(authorizationRequests: [request])
90controller.delegate = self
91controller.presentationContextProvider = self
92controller.performRequests()
93
94// Delegate -> POST the assertion to /auth/login/credential/verify.
95// (base64URLEncodedString() is a small Data extension: base64 -> URL-safe.)
96func authorizationController(controller: ASAuthorizationController,
97        didCompleteWithAuthorization auth: ASAuthorization) {
98    let a = auth.credential as! ASAuthorizationPlatformPublicKeyCredentialAssertion
99    send([
100        "rawId": a.credentialID.base64URLEncodedString(),
101        "clientDataJSON": a.rawClientDataJSON.base64URLEncodedString(),
102        "authenticatorData": a.rawAuthenticatorData.base64URLEncodedString(),
103        "signature": a.signature.base64URLEncodedString(),
104        "userHandle": a.userID.base64URLEncodedString(),
105    ])
106}`},android:{language:"Kotlin",code:`val credentialManager = CredentialManager.create(context)
107
108val option  = GetPublicKeyCredentialOption(requestJson = optionsJson)
109val request = GetCredentialRequest(listOf(option))
110
111val result     = credentialManager.getCredential(context, request)
112val credential = result.credential as PublicKeyCredential
113
114// POST the signed assertion JSON to /auth/login/credential/verify.
115sendToServer(credential.authenticationResponseJson)`},backend:{language:"Go",code:`// Begin: issue assertion options (challenge + allowed credentials).
116options, session, err := web.BeginLogin(user)
117// -> send "options" to the client; keep "session".
118
119// Finish: verify the signed assertion (signature + sign-count check).
120credential, err := web.FinishLogin(user, session, r)
121// -> bump the stored sign count; the user is now authenticated.`}},mediation:{web:{language:"TypeScript",code:`// Conditional UI surfaces passkeys in the username field's autofill menu.
122// Mark the input:  <input autocomplete="username webauthn">
123
124// Feature-detect first - not every browser supports it.
125if (await PublicKeyCredential.isConditionalMediationAvailable?.()) {
126  const opts = await api.post('/auth/login/credential/options'); // no email -> discoverable
127  opts.challenge = base64urlToBuffer(opts.challenge);
128  const controller = new AbortController();
129
130  // Stays pending; resolves only when the user picks a passkey from autofill.
131  const assertion = await navigator.credentials.get({
132    publicKey: opts,
133    mediation: 'conditional',
134    signal: controller.signal, // abort this before starting a modal ceremony
135  });
136  // ...then verify exactly like a normal assertion.
137}`},ios:{language:"Swift",code:`// AutoFill-assisted sign-in - iOS's conditional UI. Pair it with a
138// .textContentType(.username) field so passkeys appear in the QuickType bar.
139let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(
140    relyingPartyIdentifier: "test-passkeys.com")
141let request = provider.createCredentialAssertionRequest(challenge: challenge)
142
143let controller = ASAuthorizationController(authorizationRequests: [request])
144controller.delegate = self
145controller.presentationContextProvider = self
146
147// Surfaces passkeys inline instead of a modal sheet.
148controller.performAutoFillAssistedRequests()`},android:{language:"Kotlin",code:`// Conditional UI: attach a *pending* get-request to the username field
149// (androidx.credentials 1.5+ autofill integration). Matching passkeys then
150// surface in the keyboard / autofill suggestions instead of a modal sheet.
151val option  = GetPublicKeyCredentialOption(requestJson = optionsJson)
152val request = GetCredentialRequest(listOf(option))
153
154usernameField.pendingGetCredentialRequest =
155    PendingGetCredentialRequest(request) { response ->
156        // Runs only when the user picks a passkey from the suggestions.
157        val credential = response.credential as PublicKeyCredential
158        sendToServer(credential.authenticationResponseJson)
159    }`},backend:{language:"Go",code:`// Mediation is a *client* concern; the server just issues discoverable
160// assertion options - no allowed-credentials list, so the client can offer
161// any resident passkey for your RP ID.
162options, session, err := web.BeginDiscoverableLogin()
163
164// Finish takes a callback that resolves the user from the assertion:
165findUser := func(rawID, userHandle []byte) (webauthn.User, error) {
166    return lookupUserByHandle(userHandle) // your DB lookup
167}
168credential, err := web.FinishDiscoverableLogin(findUser, session, r)`}}};get selectedPlatformData(){return this.platforms.find(s=>s.id===this.selectedPlatform)}get currentSample(){return this.samples[this.currentView]?.[this.selectedPlatform]}copyCode(s){return w(this,null,function*(){try{yield navigator.clipboard.writeText(s),this.copied=!0,setTimeout(()=>this.copied=!1,1500)}catch{}})}static \u0275fac=function(i){return new(i||r)};static \u0275cmp=S({type:r,selectors:[["app-implementation-guide"]],decls:31,vars:7,consts:[[1,"subnav"],["routerLink","/learn",1,"page-back"],["name","chevron-left",3,"size"],[1,"hero"],[1,"hero-icon"],["name","lightbulb",1,"c-white",3,"size"],[1,"page","guide"],[1,"picker-label"],[1,"selector","card"],[1,"seg",3,"seg--active","background"],[1,"platforms"],[1,"platform",3,"platform--active","background"],[1,"content"],[1,"card","card--pad"],[1,"code-panel","card"],[1,"code-empty","card","card--pad"],["routerLink","/playground",1,"callout","callout--primary","try-playground"],[1,"seg",3,"click"],[3,"name","size"],[1,"platform",3,"click"],[1,"platform-name"],[1,"body"],[1,"callout","guide-tip"],[1,"c-primary"],[1,"steps"],[1,"step"],[1,"step-check"],["name","check",1,"c-primary",3,"size"],[1,"callout","callout--success","guide-tip"],[1,"c-success"],[1,"callout","callout--violet","guide-tip"],[1,"c-violet"],[1,"code-head"],[1,"code-dot"],[1,"code-title"],[1,"code-copy",3,"click"],[1,"code-body"],["name","beaker",1,"c-primary",3,"size"],["name","arrow-right",3,"size"]],template:function(i,a){if(i&1&&(e(0,"nav",0)(1,"a",1),c(2,"app-icon",2),e(3,"span"),n(4,"Back to Learn"),t()()(),e(5,"div",3)(6,"div",4),c(7,"app-icon",5),t(),e(8,"h1"),n(9,"Understanding Passkeys"),t(),e(10,"p"),n(11,"Learn how passkeys work and implement them across platforms"),t()(),e(12,"div",6)(13,"h3",7),n(14,"Choose Ceremony"),t(),e(15,"div",8),y(16,A,4,7,"button",9,D),t(),e(18,"h3",7),n(19,"Choose Platform"),t(),e(20,"div",10),y(21,q,4,7,"button",11,D),t(),e(23,"div",12),v(24,G,10,0,"div",13)(25,U,8,0,"div",13)(26,V,10,0,"div",13)(27,J,10,0,"div",13),v(28,L,12,8,"div",14)(29,F,13,3,"div",15),v(30,K,7,2,"a",16),t()()),i&2){let l,m;o(2),d("size",18),o(3),u("background",a.selectedPlatformData.gradient),o(2),d("size",32),o(9),C(a.views),o(5),C(a.platforms),o(3),b((l=a.currentView)==="overview"?24:l==="registration"?25:l==="authentication"?26:l==="mediation"?27:-1),o(4),b((m=a.currentSample)?28:29,m),o(2),b(a.currentView==="registration"||a.currentView==="authentication"?30:-1)}},dependencies:[E,z],styles:[".subnav[_ngcontent-%COMP%]{max-width:var(--content-article);margin:0 auto;padding:1.25rem var(--gutter)}.subnav[_ngcontent-%COMP%]   .page-back[_ngcontent-%COMP%]{margin-bottom:0}.hero[_ngcontent-%COMP%]{padding:3rem 1.5rem;text-align:center;color:#fff}.hero-icon[_ngcontent-%COMP%]{width:4rem;height:4rem;margin:0 auto 1rem;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-lg);background:#fff3}.hero[_ngcontent-%COMP%]   h1[_ngcontent-%COMP%]{color:#fff;font-size:1.5rem;margin-bottom:.5rem}.hero[_ngcontent-%COMP%]   p[_ngcontent-%COMP%]{max-width:28rem;margin:0 auto;font-size:.875rem;color:#ffffffe6}.guide[_ngcontent-%COMP%]{max-width:var(--content-article)}.body[_ngcontent-%COMP%]{font-size:.875rem;color:var(--muted-foreground);line-height:1.6}.selector[_ngcontent-%COMP%]{display:grid;grid-template-columns:repeat(4,1fr);gap:.375rem;padding:.375rem;margin-bottom:1.5rem}.seg[_ngcontent-%COMP%]{display:flex;flex-direction:column;align-items:center;gap:.25rem;padding:.625rem;border:none;border-radius:var(--radius);background:color-mix(in srgb,var(--muted) 60%,transparent);color:var(--muted-foreground);font-size:.625rem;cursor:pointer;transition:background .2s ease}.seg[_ngcontent-%COMP%]:hover{background:var(--muted)}.seg--active[_ngcontent-%COMP%]{color:#fff}.picker-label[_ngcontent-%COMP%]{font-size:.875rem;margin-bottom:.75rem}.platforms[_ngcontent-%COMP%]{display:grid;grid-template-columns:repeat(4,1fr);gap:.5rem;margin-bottom:2rem}@media(max-width:480px){.selector[_ngcontent-%COMP%], .platforms[_ngcontent-%COMP%]{grid-template-columns:repeat(2,1fr)}}.platform[_ngcontent-%COMP%]{display:flex;flex-direction:column;align-items:center;gap:.25rem;padding:.75rem;border:2px solid var(--border);border-radius:var(--radius);background:var(--card);color:var(--muted-foreground);font-size:.75rem;cursor:pointer;transition:border-color .15s ease}.platform[_ngcontent-%COMP%]:hover{border-color:color-mix(in srgb,var(--primary) 50%,transparent)}.platform--active[_ngcontent-%COMP%]{color:#fff;border-color:transparent}.content[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:1rem}.card--pad[_ngcontent-%COMP%]   h3[_ngcontent-%COMP%]{margin-bottom:.75rem}.guide-tip[_ngcontent-%COMP%]{margin-top:1rem;font-size:.75rem}.steps[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:.5rem;margin-top:.5rem}.step[_ngcontent-%COMP%]{display:flex;align-items:flex-start;gap:.5rem}.step-check[_ngcontent-%COMP%]{width:1.25rem;height:1.25rem;flex-shrink:0;margin-top:.125rem;display:flex;align-items:center;justify-content:center;border-radius:9999px;background:color-mix(in srgb,var(--primary) 10%,transparent)}.code-panel[_ngcontent-%COMP%]{padding:0;overflow:hidden}.code-head[_ngcontent-%COMP%]{display:flex;align-items:center;gap:.5rem;padding:.625rem .875rem;border-bottom:1px solid var(--border);background:var(--card)}
168.code-dot[_ngcontent-%COMP%]{width:.625rem;height:.625rem;flex-shrink:0;border-radius:9999px}.code-title[_ngcontent-%COMP%]{flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.8125rem;font-weight:600;color:var(--muted-foreground)}.code-copy[_ngcontent-%COMP%]{display:inline-flex;align-items:center;gap:.35rem;padding:.3rem .6rem;border:1px solid var(--border);border-radius:var(--radius);background:var(--card);color:var(--muted-foreground);font-size:.8125rem;cursor:pointer;transition:background .15s ease,color .15s ease}.code-copy[_ngcontent-%COMP%]:hover{background:var(--muted);color:var(--foreground)}.code-body[_ngcontent-%COMP%]{margin:0;padding:1rem;overflow-x:auto;background:var(--muted);color:var(--foreground);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.8125rem;line-height:1.55;-moz-tab-size:2;tab-size:2}.code-body[_ngcontent-%COMP%]   code[_ngcontent-%COMP%]{font:inherit}.code-empty[_ngcontent-%COMP%]{display:flex;align-items:center;gap:.625rem}.code-empty[_ngcontent-%COMP%]   p[_ngcontent-%COMP%]{font-size:.875rem;color:var(--muted-foreground)}.try-playground[_ngcontent-%COMP%]{align-items:center;margin-top:1rem;text-decoration:none;transition:border-color .15s ease,background .15s ease}.try-playground[_ngcontent-%COMP%]:hover{border-color:color-mix(in srgb,var(--primary) 55%,transparent);background:color-mix(in srgb,var(--primary) 10%,transparent)}.try-playground[_ngcontent-%COMP%]   span[_ngcontent-%COMP%]{flex:1}.try-playground[_ngcontent-%COMP%]   app-icon[_ngcontent-%COMP%]:last-child{color:var(--primary)}"]})};export{R as ImplementationGuideComponent};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.