@auth/core
Version:
Authentication for the Web.
101 lines (92 loc) • 2.6 kB
text/typescript
import type {
InternalOptions,
RequestInternal,
ResponseInternal,
User,
} from "../../types.js"
import type { Cookie, SessionStore } from "../utils/cookie.js"
import { getLoggedInUser } from "../utils/session.js"
import {
assertInternalOptionsWebAuthn,
inferWebAuthnOptions,
getAuthenticationResponse,
getRegistrationResponse,
} from "../utils/webauthn-utils.js"
/**
* Returns authentication or registration options for a WebAuthn flow
* depending on the parameters provided.
*/
export async function webAuthnOptions(
request: RequestInternal,
options: InternalOptions,
sessionStore: SessionStore,
cookies: Cookie[]
// @ts-expect-error issue with returning from a switch case
): Promise<ResponseInternal> {
// Return an error if the adapter is missing or if the provider
// is not a webauthn provider.
const narrowOptions = assertInternalOptionsWebAuthn(options)
const { provider } = narrowOptions
// Extract the action from the query parameters
const { action } = (request.query ?? {}) as Record<string, unknown>
// Action must be either "register", "authenticate", or undefined
if (
action !== "register" &&
action !== "authenticate" &&
typeof action !== "undefined"
) {
return {
status: 400,
body: { error: "Invalid action" },
cookies,
headers: {
"Content-Type": "application/json",
},
}
}
// Get the user info from the session
const sessionUser = await getLoggedInUser(options, sessionStore)
// Extract user info from request
// If session user exists, we don't need to call getUserInfo
const getUserInfoResponse = sessionUser
? {
user: sessionUser,
exists: true,
}
: await provider.getUserInfo(options, request)
const userInfo = getUserInfoResponse?.user
// Make a decision on what kind of webauthn options to return
const decision = inferWebAuthnOptions(
action,
!!sessionUser,
getUserInfoResponse
)
switch (decision) {
case "authenticate":
return getAuthenticationResponse(
narrowOptions,
request,
userInfo,
cookies
)
case "register":
if (typeof userInfo?.email === "string") {
return getRegistrationResponse(
narrowOptions,
request,
userInfo as User & { email: string },
cookies
)
}
break
default:
return {
status: 400,
body: { error: "Invalid request" },
cookies,
headers: {
"Content-Type": "application/json",
},
}
}
}