mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
1 Commits
@auth/soli
...
fix/sign-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d25bb5d934 |
@@ -1,7 +1,7 @@
|
|||||||
|
import * as checks from "./checks.js"
|
||||||
import * as o from "oauth4webapi"
|
import * as o from "oauth4webapi"
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
CookiesOptions,
|
|
||||||
InternalOptions,
|
InternalOptions,
|
||||||
RequestInternal,
|
RequestInternal,
|
||||||
ResponseInternal,
|
ResponseInternal,
|
||||||
@@ -58,10 +58,10 @@ export async function getAuthorizationUrl(
|
|||||||
|
|
||||||
const cookies: Cookie[] = []
|
const cookies: Cookie[] = []
|
||||||
|
|
||||||
if (provider.checks?.includes("state")) {
|
const state = await checks.state.create(options)
|
||||||
const { value, raw } = await createState(options)
|
if (state) {
|
||||||
authParams.set("state", raw)
|
authParams.set("state", state.value)
|
||||||
cookies.push(value)
|
cookies.push(state.cookie)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (provider.checks?.includes("pkce")) {
|
if (provider.checks?.includes("pkce")) {
|
||||||
@@ -70,17 +70,17 @@ export async function getAuthorizationUrl(
|
|||||||
// a random `nonce` must be used for CSRF protection.
|
// a random `nonce` must be used for CSRF protection.
|
||||||
provider.checks = ["nonce"]
|
provider.checks = ["nonce"]
|
||||||
} else {
|
} else {
|
||||||
const { code_challenge, pkce } = await createPKCE(options)
|
const { value, cookie } = await checks.pkce.create(options)
|
||||||
authParams.set("code_challenge", code_challenge)
|
authParams.set("code_challenge", value)
|
||||||
authParams.set("code_challenge_method", "S256")
|
authParams.set("code_challenge_method", "S256")
|
||||||
cookies.push(pkce)
|
cookies.push(cookie)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (provider.checks?.includes("nonce")) {
|
const nonce = await checks.nonce.create(options)
|
||||||
const nonce = await createNonce(options)
|
if (nonce) {
|
||||||
authParams.set("nonce", nonce.value)
|
authParams.set("nonce", nonce.value)
|
||||||
cookies.push(nonce)
|
cookies.push(nonce.cookie)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This does not work in normalizeOAuth because authorization endpoint can come from discovery
|
// TODO: This does not work in normalizeOAuth because authorization endpoint can come from discovery
|
||||||
@@ -92,52 +92,3 @@ export async function getAuthorizationUrl(
|
|||||||
logger.debug("authorization url is ready", { url, cookies, provider })
|
logger.debug("authorization url is ready", { url, cookies, provider })
|
||||||
return { redirect: url, cookies }
|
return { redirect: url, cookies }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a signed cookie. */
|
|
||||||
export async function signCookie(
|
|
||||||
type: keyof CookiesOptions,
|
|
||||||
value: string,
|
|
||||||
maxAge: number,
|
|
||||||
options: InternalOptions<"oauth">
|
|
||||||
): Promise<Cookie> {
|
|
||||||
const { cookies, jwt, logger } = options
|
|
||||||
|
|
||||||
logger.debug(`CREATE_${type.toUpperCase()}`, { value, maxAge })
|
|
||||||
|
|
||||||
const expires = new Date()
|
|
||||||
expires.setTime(expires.getTime() + maxAge * 1000)
|
|
||||||
return {
|
|
||||||
name: cookies[type].name,
|
|
||||||
value: await jwt.encode({ ...jwt, maxAge, token: { value } }),
|
|
||||||
options: { ...cookies[type].options, expires },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const STATE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
|
||||||
async function createState(options: InternalOptions<"oauth">) {
|
|
||||||
const raw = o.generateRandomState()
|
|
||||||
const maxAge = STATE_MAX_AGE
|
|
||||||
const value = await signCookie("state", raw, maxAge, options)
|
|
||||||
return { value, raw }
|
|
||||||
}
|
|
||||||
|
|
||||||
const PKCE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
|
||||||
async function createPKCE(options: InternalOptions<"oauth">) {
|
|
||||||
const code_verifier = o.generateRandomCodeVerifier()
|
|
||||||
const code_challenge = await o.calculatePKCECodeChallenge(code_verifier)
|
|
||||||
const maxAge = PKCE_MAX_AGE
|
|
||||||
const pkce = await signCookie(
|
|
||||||
"pkceCodeVerifier",
|
|
||||||
code_verifier,
|
|
||||||
maxAge,
|
|
||||||
options
|
|
||||||
)
|
|
||||||
return { code_challenge, pkce }
|
|
||||||
}
|
|
||||||
|
|
||||||
const NONCE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
|
||||||
async function createNonce(options: InternalOptions<"oauth">) {
|
|
||||||
const raw = o.generateRandomNonce()
|
|
||||||
const maxAge = NONCE_MAX_AGE
|
|
||||||
return await signCookie("nonce", raw, maxAge, options)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
|
import * as checks from "./checks.js"
|
||||||
import * as o from "oauth4webapi"
|
import * as o from "oauth4webapi"
|
||||||
import { OAuthCallbackError, OAuthProfileParseError } from "../../errors.js"
|
import { OAuthCallbackError, OAuthProfileParseError } from "../../errors.js"
|
||||||
import { useNonce } from "./nonce-handler.js"
|
|
||||||
import { usePKCECodeVerifier } from "./pkce-handler.js"
|
|
||||||
import { useState } from "./state-handler.js"
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
InternalOptions,
|
InternalOptions,
|
||||||
@@ -73,7 +71,7 @@ export async function handleOAuth(
|
|||||||
|
|
||||||
const resCookies: Cookie[] = []
|
const resCookies: Cookie[] = []
|
||||||
|
|
||||||
const state = await useState(cookies, resCookies, options)
|
const state = await checks.state.use(cookies, resCookies, options)
|
||||||
|
|
||||||
const parameters = o.validateAuthResponse(
|
const parameters = o.validateAuthResponse(
|
||||||
as,
|
as,
|
||||||
@@ -91,7 +89,7 @@ export async function handleOAuth(
|
|||||||
throw new OAuthCallbackError(parameters.error)
|
throw new OAuthCallbackError(parameters.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
const codeVerifier = await usePKCECodeVerifier(
|
const codeVerifier = await checks.pkce.use(
|
||||||
cookies?.[options.cookies.pkceCodeVerifier.name],
|
cookies?.[options.cookies.pkceCodeVerifier.name],
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
@@ -99,7 +97,10 @@ export async function handleOAuth(
|
|||||||
if (codeVerifier) resCookies.push(codeVerifier.cookie)
|
if (codeVerifier) resCookies.push(codeVerifier.cookie)
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
const nonce = await useNonce(cookies?.[options.cookies.nonce.name], options)
|
const nonce = await checks.nonce.use(
|
||||||
|
cookies?.[options.cookies.nonce.name],
|
||||||
|
options
|
||||||
|
)
|
||||||
if (nonce && provider.type === "oidc") {
|
if (nonce && provider.type === "oidc") {
|
||||||
resCookies.push(nonce.cookie)
|
resCookies.push(nonce.cookie)
|
||||||
}
|
}
|
||||||
|
|||||||
155
packages/core/src/lib/oauth/checks.ts
Normal file
155
packages/core/src/lib/oauth/checks.ts
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
import * as o from "oauth4webapi"
|
||||||
|
import * as jwt from "../../jwt.js"
|
||||||
|
|
||||||
|
import type {
|
||||||
|
InternalOptions,
|
||||||
|
RequestInternal,
|
||||||
|
CookiesOptions,
|
||||||
|
} from "../../types.js"
|
||||||
|
import type { Cookie } from "../cookie.js"
|
||||||
|
|
||||||
|
import { InvalidState } from "../../errors.js"
|
||||||
|
|
||||||
|
/** Returns a signed cookie. */
|
||||||
|
export async function signCookie(
|
||||||
|
type: keyof CookiesOptions,
|
||||||
|
value: string,
|
||||||
|
maxAge: number,
|
||||||
|
options: InternalOptions<"oauth">
|
||||||
|
): Promise<Cookie> {
|
||||||
|
const { cookies, logger } = options
|
||||||
|
|
||||||
|
logger.debug(`CREATE_${type.toUpperCase()}`, { value, maxAge })
|
||||||
|
|
||||||
|
const expires = new Date()
|
||||||
|
expires.setTime(expires.getTime() + maxAge * 1000)
|
||||||
|
return {
|
||||||
|
name: cookies[type].name,
|
||||||
|
value: await jwt.encode({ ...options.jwt, maxAge, token: { value } }),
|
||||||
|
options: { ...cookies[type].options, expires },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PKCE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
||||||
|
export const pkce = {
|
||||||
|
async create(options: InternalOptions<"oauth">) {
|
||||||
|
const code_verifier = o.generateRandomCodeVerifier()
|
||||||
|
const value = await o.calculatePKCECodeChallenge(code_verifier)
|
||||||
|
const maxAge = PKCE_MAX_AGE
|
||||||
|
const cookie = await signCookie(
|
||||||
|
"pkceCodeVerifier",
|
||||||
|
code_verifier,
|
||||||
|
maxAge,
|
||||||
|
options
|
||||||
|
)
|
||||||
|
return { cookie, value }
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns code_verifier if provider uses PKCE,
|
||||||
|
* and clears the container cookie afterwards.
|
||||||
|
*/
|
||||||
|
async use(
|
||||||
|
codeVerifier: string | undefined,
|
||||||
|
options: InternalOptions<"oauth">
|
||||||
|
): Promise<{ codeVerifier: string; cookie: Cookie } | undefined> {
|
||||||
|
const { cookies, provider } = options
|
||||||
|
|
||||||
|
if (!provider?.checks?.includes("pkce") || !codeVerifier) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const pkce = (await jwt.decode({
|
||||||
|
...options.jwt,
|
||||||
|
token: codeVerifier,
|
||||||
|
})) as any
|
||||||
|
|
||||||
|
return {
|
||||||
|
codeVerifier: pkce?.value ?? undefined,
|
||||||
|
cookie: {
|
||||||
|
name: cookies.pkceCodeVerifier.name,
|
||||||
|
value: "",
|
||||||
|
options: { ...cookies.pkceCodeVerifier.options, maxAge: 0 },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const STATE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
||||||
|
export const state = {
|
||||||
|
async create(options: InternalOptions<"oauth">) {
|
||||||
|
if (!options.provider.checks.includes("state")) return
|
||||||
|
// TODO: support customizing the state
|
||||||
|
const value = o.generateRandomState()
|
||||||
|
const maxAge = STATE_MAX_AGE
|
||||||
|
const cookie = await signCookie("state", value, maxAge, options)
|
||||||
|
return { cookie, value }
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Returns state from the saved cookie
|
||||||
|
* if the provider supports states,
|
||||||
|
* and clears the container cookie afterwards.
|
||||||
|
*/
|
||||||
|
async use(
|
||||||
|
cookies: RequestInternal["cookies"],
|
||||||
|
resCookies: Cookie[],
|
||||||
|
options: InternalOptions<"oauth">
|
||||||
|
): Promise<string | undefined> {
|
||||||
|
const { provider, jwt } = options
|
||||||
|
if (!provider.checks.includes("state")) return
|
||||||
|
|
||||||
|
const state = cookies?.[options.cookies.state.name]
|
||||||
|
|
||||||
|
if (!state) throw new InvalidState("State was missing from the cookies.")
|
||||||
|
|
||||||
|
// IDEA: Let the user do something with the returned state
|
||||||
|
const value = (await jwt.decode({ ...options.jwt, token: state })) as any
|
||||||
|
|
||||||
|
if (!value?.value) throw new InvalidState("Could not parse state cookie.")
|
||||||
|
|
||||||
|
// Clear the state cookie after use
|
||||||
|
resCookies.push({
|
||||||
|
name: options.cookies.state.name,
|
||||||
|
value: "",
|
||||||
|
options: { ...options.cookies.state.options, maxAge: 0 },
|
||||||
|
})
|
||||||
|
|
||||||
|
return value.value
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const NONCE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
||||||
|
export const nonce = {
|
||||||
|
async create(options: InternalOptions<"oauth">) {
|
||||||
|
if (!options.provider.checks.includes("nonce")) return
|
||||||
|
const value = o.generateRandomNonce()
|
||||||
|
const maxAge = NONCE_MAX_AGE
|
||||||
|
const cookie = await signCookie("nonce", value, maxAge, options)
|
||||||
|
return { cookie, value }
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Returns nonce from if the provider supports nonce,
|
||||||
|
* and clears the container cookie afterwards.
|
||||||
|
*/
|
||||||
|
async use(
|
||||||
|
nonce: string | undefined,
|
||||||
|
options: InternalOptions<"oauth">
|
||||||
|
): Promise<{ value: string; cookie: Cookie } | undefined> {
|
||||||
|
const { cookies, provider } = options
|
||||||
|
|
||||||
|
if (!provider?.checks?.includes("nonce") || !nonce) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = (await jwt.decode({ ...options.jwt, token: nonce })) as any
|
||||||
|
|
||||||
|
return {
|
||||||
|
value: value?.value ?? undefined,
|
||||||
|
cookie: {
|
||||||
|
name: cookies.nonce.name,
|
||||||
|
value: "",
|
||||||
|
options: { ...cookies.nonce.options, maxAge: 0 },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
import * as o from "oauth4webapi"
|
|
||||||
import * as jwt from "../../jwt.js"
|
|
||||||
|
|
||||||
import type { InternalOptions } from "../../types.js"
|
|
||||||
import type { Cookie } from "../cookie.js"
|
|
||||||
|
|
||||||
const NONCE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns nonce if the provider supports it
|
|
||||||
* and saves it in a cookie
|
|
||||||
*/
|
|
||||||
export async function createNonce(options: InternalOptions<"oauth">): Promise<
|
|
||||||
| undefined
|
|
||||||
| {
|
|
||||||
value: string
|
|
||||||
cookie: Cookie
|
|
||||||
}
|
|
||||||
> {
|
|
||||||
const { cookies, logger, provider } = options
|
|
||||||
if (!provider.checks?.includes("nonce")) {
|
|
||||||
// Provider does not support nonce, return nothing.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const nonce = o.generateRandomNonce()
|
|
||||||
|
|
||||||
const expires = new Date()
|
|
||||||
expires.setTime(expires.getTime() + NONCE_MAX_AGE * 1000)
|
|
||||||
|
|
||||||
// Encrypt nonce and save it to an encrypted cookie
|
|
||||||
const encryptedNonce = await jwt.encode({
|
|
||||||
...options.jwt,
|
|
||||||
maxAge: NONCE_MAX_AGE,
|
|
||||||
token: { nonce },
|
|
||||||
})
|
|
||||||
|
|
||||||
logger.debug("CREATE_ENCRYPTED_NONCE", {
|
|
||||||
nonce,
|
|
||||||
maxAge: NONCE_MAX_AGE,
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
cookie: {
|
|
||||||
name: cookies.nonce.name,
|
|
||||||
value: encryptedNonce,
|
|
||||||
options: { ...cookies.nonce.options, expires },
|
|
||||||
},
|
|
||||||
value: nonce,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns nonce from if the provider supports nonce,
|
|
||||||
* and clears the container cookie afterwards.
|
|
||||||
*/
|
|
||||||
export async function useNonce(
|
|
||||||
nonce: string | undefined,
|
|
||||||
options: InternalOptions<"oauth">
|
|
||||||
): Promise<{ value: string; cookie: Cookie } | undefined> {
|
|
||||||
const { cookies, provider } = options
|
|
||||||
|
|
||||||
if (!provider?.checks?.includes("nonce") || !nonce) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const value = (await jwt.decode({ ...options.jwt, token: nonce })) as any
|
|
||||||
|
|
||||||
return {
|
|
||||||
value: value?.value ?? undefined,
|
|
||||||
cookie: {
|
|
||||||
name: cookies.nonce.name,
|
|
||||||
value: "",
|
|
||||||
options: { ...cookies.nonce.options, maxAge: 0 },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
import * as o from "oauth4webapi"
|
|
||||||
import * as jwt from "../../jwt.js"
|
|
||||||
|
|
||||||
import type { InternalOptions } from "../../types.js"
|
|
||||||
import type { Cookie } from "../cookie.js"
|
|
||||||
|
|
||||||
const PKCE_CODE_CHALLENGE_METHOD = "S256"
|
|
||||||
const PKCE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns `code_challenge` and `code_challenge_method`
|
|
||||||
* and saves them in a cookie.
|
|
||||||
*/
|
|
||||||
export async function createPKCE(options: InternalOptions<"oauth">): Promise<
|
|
||||||
| undefined
|
|
||||||
| {
|
|
||||||
code_challenge: string
|
|
||||||
code_challenge_method: "S256"
|
|
||||||
cookie: Cookie
|
|
||||||
}
|
|
||||||
> {
|
|
||||||
const { cookies, logger, provider } = options
|
|
||||||
if (!provider.checks?.includes("pkce")) {
|
|
||||||
// Provider does not support PKCE, return nothing.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const code_verifier = o.generateRandomCodeVerifier()
|
|
||||||
const code_challenge = await o.calculatePKCECodeChallenge(code_verifier)
|
|
||||||
|
|
||||||
const maxAge = cookies.pkceCodeVerifier.options.maxAge ?? PKCE_MAX_AGE
|
|
||||||
|
|
||||||
const expires = new Date()
|
|
||||||
expires.setTime(expires.getTime() + maxAge * 1000)
|
|
||||||
|
|
||||||
// Encrypt code_verifier and save it to an encrypted cookie
|
|
||||||
const encryptedCodeVerifier = await jwt.encode({
|
|
||||||
...options.jwt,
|
|
||||||
maxAge,
|
|
||||||
token: { code_verifier },
|
|
||||||
})
|
|
||||||
|
|
||||||
logger.debug("CREATE_PKCE_CHALLENGE_VERIFIER", {
|
|
||||||
code_challenge,
|
|
||||||
code_challenge_method: PKCE_CODE_CHALLENGE_METHOD,
|
|
||||||
code_verifier,
|
|
||||||
maxAge,
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
code_challenge,
|
|
||||||
code_challenge_method: PKCE_CODE_CHALLENGE_METHOD,
|
|
||||||
cookie: {
|
|
||||||
name: cookies.pkceCodeVerifier.name,
|
|
||||||
value: encryptedCodeVerifier,
|
|
||||||
options: { ...cookies.pkceCodeVerifier.options, expires },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns code_verifier if provider uses PKCE,
|
|
||||||
* and clears the container cookie afterwards.
|
|
||||||
*/
|
|
||||||
export async function usePKCECodeVerifier(
|
|
||||||
codeVerifier: string | undefined,
|
|
||||||
options: InternalOptions<"oauth">
|
|
||||||
): Promise<{ codeVerifier: string; cookie: Cookie } | undefined> {
|
|
||||||
const { cookies, provider } = options
|
|
||||||
|
|
||||||
if (!provider?.checks?.includes("pkce") || !codeVerifier) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const pkce = (await jwt.decode({
|
|
||||||
...options.jwt,
|
|
||||||
token: codeVerifier,
|
|
||||||
})) as any
|
|
||||||
|
|
||||||
return {
|
|
||||||
codeVerifier: pkce?.value ?? undefined,
|
|
||||||
cookie: {
|
|
||||||
name: cookies.pkceCodeVerifier.name,
|
|
||||||
value: "",
|
|
||||||
options: { ...cookies.pkceCodeVerifier.options, maxAge: 0 },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import * as o from "oauth4webapi"
|
|
||||||
import type { InternalOptions, RequestInternal } from "../../types.js"
|
|
||||||
import type { Cookie } from "../cookie.js"
|
|
||||||
import { InvalidState } from "../../errors.js"
|
|
||||||
|
|
||||||
const STATE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
|
||||||
|
|
||||||
/** Returns state if the provider supports it */
|
|
||||||
export async function createState(
|
|
||||||
options: InternalOptions<"oauth">
|
|
||||||
): Promise<{ cookie: Cookie; value: string } | undefined> {
|
|
||||||
const { logger, provider, jwt, cookies } = options
|
|
||||||
|
|
||||||
if (!provider.checks?.includes("state")) {
|
|
||||||
// Provider does not support state, return nothing
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const state = o.generateRandomState()
|
|
||||||
const maxAge = cookies.state.options.maxAge ?? STATE_MAX_AGE
|
|
||||||
|
|
||||||
const encodedState = await jwt.encode({
|
|
||||||
...jwt,
|
|
||||||
maxAge,
|
|
||||||
token: { state },
|
|
||||||
})
|
|
||||||
|
|
||||||
logger.debug("CREATE_STATE", { state, maxAge })
|
|
||||||
|
|
||||||
const expires = new Date()
|
|
||||||
expires.setTime(expires.getTime() + maxAge * 1000)
|
|
||||||
return {
|
|
||||||
value: state,
|
|
||||||
cookie: {
|
|
||||||
name: cookies.state.name,
|
|
||||||
value: encodedState,
|
|
||||||
options: { ...cookies.state.options, expires },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns state from the saved cookie
|
|
||||||
* if the provider supports states,
|
|
||||||
* and clears the container cookie afterwards.
|
|
||||||
*/
|
|
||||||
export async function useState(
|
|
||||||
cookies: RequestInternal["cookies"],
|
|
||||||
resCookies: Cookie[],
|
|
||||||
options: InternalOptions<"oauth">
|
|
||||||
): Promise<string | undefined> {
|
|
||||||
const { provider, jwt } = options
|
|
||||||
if (!provider.checks.includes("state")) return
|
|
||||||
|
|
||||||
const state = cookies?.[options.cookies.state.name]
|
|
||||||
|
|
||||||
if (!state) throw new InvalidState("State was missing from the cookies.")
|
|
||||||
|
|
||||||
// IDEA: Let the user do something with the returned state
|
|
||||||
const value = (await jwt.decode({ ...options.jwt, token: state })) as any
|
|
||||||
|
|
||||||
if (!value?.value) throw new InvalidState("Could not parse state cookie.")
|
|
||||||
|
|
||||||
// Clear the state cookie after use
|
|
||||||
resCookies.push({
|
|
||||||
name: options.cookies.state.name,
|
|
||||||
value: "",
|
|
||||||
options: { ...options.cookies.state.options, maxAge: 0 },
|
|
||||||
})
|
|
||||||
|
|
||||||
return value.value
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user