Skip to content

Commit

Permalink
Add WebAuthn implementation to SvelteKit
Browse files Browse the repository at this point in the history
  • Loading branch information
scotttrinh committed Mar 21, 2024
1 parent 2677fa4 commit c64c7e7
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 0 deletions.
9 changes: 9 additions & 0 deletions packages/auth-sveltekit/src/client.ts
@@ -1,4 +1,5 @@
import type { BuiltinOAuthProviderNames } from "@edgedb/auth-core";
import { WebAuthnClient } from "@edgedb/auth-core/webauthn";

export interface AuthOptions {
baseUrl: string;
Expand Down Expand Up @@ -35,10 +36,18 @@ export default function createClientAuth(options: AuthOptions) {

export class ClientAuth {
protected readonly config: AuthConfig;
readonly webAuthnClient: WebAuthnClient;

/** @internal */
constructor(options: AuthOptions) {
this.config = getConfig(options);
this.webAuthnClient = new WebAuthnClient({
signupOptionsUrl: `${this.config.authRoute}/webauthn/signup/options`,
signupUrl: `${this.config.authRoute}/webauthn/signup`,
signinOptionsUrl: `${this.config.authRoute}/webauthn/signin/options`,
signinUrl: `${this.config.authRoute}/webauthn/signin`,
verifyUrl: `${this.config.authRoute}/webauthn/verify`,
});
}

getOAuthUrl(providerName: BuiltinOAuthProviderNames) {
Expand Down
101 changes: 101 additions & 0 deletions packages/auth-sveltekit/src/server.ts
Expand Up @@ -17,6 +17,8 @@ import {
InvalidDataError,
OAuthProviderFailureError,
EdgeDBAuthError,
type RegistrationResponseJSON,
type AuthenticationResponseJSON,
} from "@edgedb/auth-core";
import {
ClientAuth,
Expand Down Expand Up @@ -282,6 +284,45 @@ export class ServerRequestAuth extends ClientAuth {
this.setVerifierCookie(verifier);
}

async webAuthnSignUp(data: {
email: string;
credentials: RegistrationResponseJSON;
verify_url: string;
user_handle: string;
}): Promise<{ tokenData: TokenData | null }> {
const {
email,
credentials,
verify_url: verifyUrl,
user_handle: userHandle,
} = data;

const result = await (
await this.core
).signupWithWebAuthn(email, credentials, verifyUrl, userHandle);

this.setVerifierCookie(result.verifier);
if (result.status === "complete") {
this.setAuthTokenCookie(result.tokenData.auth_token);
return { tokenData: result.tokenData };
}

return { tokenData: null };
}

async webAuthnSignIn(data: {
email: string;
assertion: AuthenticationResponseJSON;
}): Promise<{ tokenData: TokenData | null }> {
const { email, assertion } = data;
const tokenData = await (
await this.core
).signinWithWebAuthn(email, assertion);

this.setAuthTokenCookie(tokenData.auth_token);
return { tokenData };
}

async signout(): Promise<void> {
this.deleteAuthTokenCookie();
}
Expand Down Expand Up @@ -653,6 +694,66 @@ async function handleAuthRoutes(
});
}

case "webauthn/signup/options": {
const email = searchParams.get("email");
if (!email) {
throw new InvalidDataError("email missing");
}
return redirect(302, (await core).getWebAuthnSignupOptionsUrl(email));
}

case "webauthn/signin/options": {
const email = searchParams.get("email");
if (!email) {
throw new InvalidDataError("email missing");
}
return redirect(302, (await core).getWebAuthnSigninOptionsUrl(email));
}

case "webauthn/verify": {
if (!onEmailVerify) {
throw new ConfigurationError(
`'onEmailVerify' auth route handler not configured`
);
}

const verificationToken = searchParams.get("verification_token");
if (!verificationToken) {
return onEmailVerify({
error: new InvalidDataError("verification_token missing"),
});
}
const verifier = cookies.get(config.pkceVerifierCookieName);
if (!verifier) {
return onEmailVerify({
error: new PKCEError("no pkce verifier cookie found"),
verificationToken,
});
}
let tokenData: TokenData;
try {
tokenData = await (
await core
).verifyWebAuthnSignup(verificationToken, verifier);
} catch (err) {
return onEmailVerify({
error: err instanceof Error ? err : new Error(String(err)),
verificationToken,
});
}

cookies.set(config.authCookieName, tokenData.auth_token, {
httpOnly: true,
sameSite: "strict",
path: "/",
});

return onEmailVerify({
error: null,
tokenData,
});
}

case "signout": {
if (!onSignout) {
throw new ConfigurationError(
Expand Down

0 comments on commit c64c7e7

Please sign in to comment.