WebAuthnService requires a WebAuthnSession — any object with getChallenge, setChallenge, and clearChallenge. You can use SessionManager or a custom store:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import{SessionManager}from'streetjs';constsessions=newSessionManager({secret:process.env.SESSION_KEY!});// Adapter to WebAuthnSession interface:constwebAuthnSession={asyncgetChallenge(userId:string){constdata=awaitsessions.getRaw(userId);returndata??null;},asyncsetChallenge(userId:string,challenge:string,expiresAt:number){awaitsessions.setRaw(userId,{challenge,expiresAt});},asyncclearChallenge(userId:string){awaitsessions.destroyRaw(userId);},};
3. Create the service
1
2
3
4
5
6
7
8
9
10
11
import{WebAuthnService}from'streetjs';constwebAuthn=newWebAuthnService({rpName:'My App',rpId:'myapp.com',// Must match the domain used in the browserorigin:'https://myapp.com',// Must match exactly},pool,webAuthnSession,);
// GET /auth/passkey/register/optionsapp.use(async (ctx,next)=>{if (ctx.method==='GET'&&ctx.path==='/auth/passkey/register/options'){constuserId=ctx.user!.id;constoptions=awaitwebAuthn.startRegistration(userId);ctx.json(options);return;}awaitnext();});// POST /auth/passkey/register/finishapp.use(async (ctx,next)=>{if (ctx.method==='POST'&&ctx.path==='/auth/passkey/register/finish'){constuserId=ctx.user!.id;constresult=awaitwebAuthn.finishRegistration(userId,ctx.body);ctx.json({credentialId:result.credentialId},201);return;}awaitnext();});
// GET /auth/passkey/login/optionsapp.use(async (ctx,next)=>{if (ctx.method==='GET'&&ctx.path==='/auth/passkey/login/options'){const{userId}=ctx.queryas {userId:string};constoptions=awaitwebAuthn.startAuthentication(userId);ctx.json(options);return;}awaitnext();});// POST /auth/passkey/login/finishapp.use(async (ctx,next)=>{if (ctx.method==='POST'&&ctx.path==='/auth/passkey/login/finish'){const{userId}=ctx.queryas {userId:string};constresult=awaitwebAuthn.finishAuthentication(userId,ctx.body);if (result.verified){// Issue JWT or create server sessionconsttoken=jwt.sign({sub:userId});ctx.json({token});}else{ctx.json({error:'Authentication failed'},401);}return;}awaitnext();});
COSE Key Storage
StreetJS stores public keys as JWK JSON strings in the database. During registration, parseCredentialPublicKey() reads the COSE EC2 key from authData and converts it: