Use StaticWebAppsAuthContextCookie instead of just Nonce cookie and support honor post_login_redirect_uri
This commit is contained in:
Родитель
fcf764cc1b
Коммит
75211b2ed4
|
@ -41,7 +41,7 @@ export type SWACommand = typeof SWA_COMMANDS[number];
|
|||
|
||||
export const SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB = 20; // 20kb
|
||||
|
||||
export const NONCE = `Nonce`;
|
||||
export const SWA_AUTH_CONTEXT_COOKIE = `StaticWebAppsAuthContextCookie`;
|
||||
export const SWA_AUTH_COOKIE = `StaticWebAppsAuthCookie`;
|
||||
export const ALLOWED_HTTP_METHODS_FOR_STATIC_CONTENT = ["GET", "HEAD", "OPTIONS"];
|
||||
|
||||
|
|
|
@ -38,3 +38,14 @@ export function isNonceExpired(nonce: string) {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function extractPostLoginRedirectUri(protocol?: string, host?: string, path?: string) {
|
||||
if (!!protocol && !!host && !!path) {
|
||||
try {
|
||||
const url = new URL(`${protocol}://${host}${path}`);
|
||||
return url.searchParams.get("post_login_redirect_uri") ?? undefined;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import chalk from "chalk";
|
||||
import cookie from "cookie";
|
||||
import { NONCE, SWA_AUTH_COOKIE } from "../constants";
|
||||
import { SWA_AUTH_CONTEXT_COOKIE, SWA_AUTH_COOKIE } from "../constants";
|
||||
import { logger } from "./logger";
|
||||
|
||||
/**
|
||||
|
@ -35,7 +35,7 @@ export function serializeCookie(cookieName: string, cookieValue: string, options
|
|||
* @returns A ClientPrincipal object.
|
||||
*/
|
||||
export function decodeCookie(cookieValue: string): ClientPrincipal | null {
|
||||
logger.silly(`decoding cookie`);
|
||||
logger.silly(`decoding StaticWebAppsAuthCookie cookie`);
|
||||
const cookies = cookie.parse(cookieValue);
|
||||
if (cookies[SWA_AUTH_COOKIE]) {
|
||||
const decodedValue = Buffer.from(cookies[SWA_AUTH_COOKIE], "base64").toString();
|
||||
|
@ -47,32 +47,32 @@ export function decodeCookie(cookieValue: string): ClientPrincipal | null {
|
|||
}
|
||||
|
||||
/**
|
||||
* Check if the Nonce is available.
|
||||
* Check if the StaticWebAppsAuthContextCookie is available.
|
||||
* @param cookieValue The cookie value.
|
||||
* @returns True if Nonce is found. False otherwise.
|
||||
* @returns True if StaticWebAppsAuthContextCookie is found. False otherwise.
|
||||
*/
|
||||
export function validateNonceCookie(cookieValue: string | number | string[]) {
|
||||
export function validateAuthContextCookie(cookieValue: string | number | string[]) {
|
||||
if (typeof cookieValue !== "string") {
|
||||
throw Error(`TypeError: cookie value must be a string`);
|
||||
}
|
||||
|
||||
const cookies = cookie.parse(cookieValue);
|
||||
return !!cookies[NONCE];
|
||||
return !!cookies[SWA_AUTH_CONTEXT_COOKIE];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param cookieValue
|
||||
* @returns Nonce string.
|
||||
* @returns StaticWebAppsAuthContextCookie string.
|
||||
*/
|
||||
export function decodeNonceCookie(cookieValue: string): string | null {
|
||||
logger.silly(`decoding nonce cookie`);
|
||||
export function decodeAuthContextCookie(cookieValue: string): AuthContext | null {
|
||||
logger.silly(`decoding StaticWebAppsAuthContextCookie cookie`);
|
||||
const cookies = cookie.parse(cookieValue);
|
||||
if (cookies[NONCE]) {
|
||||
const decodedValue = Buffer.from(cookies[NONCE], "base64").toString();
|
||||
logger.silly(` - Nonce: ${chalk.yellow(decodedValue)}`);
|
||||
return decodedValue;
|
||||
if (cookies[SWA_AUTH_CONTEXT_COOKIE]) {
|
||||
const decodedValue = Buffer.from(cookies[SWA_AUTH_CONTEXT_COOKIE], "base64").toString();
|
||||
logger.silly(` - StaticWebAppsAuthContextCookie: ${chalk.yellow(decodedValue)}`);
|
||||
return JSON.parse(decodedValue);
|
||||
}
|
||||
logger.silly(` - no cookie 'Nonce' found`);
|
||||
logger.silly(` - no cookie 'StaticWebAppsAuthContextCookie' found`);
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { decodeNonceCookie, parseUrl, response, validateNonceCookie } from "../../../core";
|
||||
import { decodeAuthContextCookie, parseUrl, response, validateAuthContextCookie } from "../../../core";
|
||||
import * as http from "http";
|
||||
import * as https from "https";
|
||||
import * as querystring from "querystring";
|
||||
|
@ -374,7 +374,7 @@ const httpTrigger = async function (context: Context, request: http.IncomingMess
|
|||
|
||||
const { cookie } = request.headers;
|
||||
|
||||
if (!cookie || !validateNonceCookie(cookie)) {
|
||||
if (!cookie || !validateAuthContextCookie(cookie)) {
|
||||
context.res = response({
|
||||
context,
|
||||
status: 401,
|
||||
|
@ -389,9 +389,9 @@ const httpTrigger = async function (context: Context, request: http.IncomingMess
|
|||
const codeValue = url.searchParams.get("code");
|
||||
const stateValue = url.searchParams.get("state");
|
||||
|
||||
const nonce = decodeNonceCookie(cookie);
|
||||
const authContext = decodeAuthContextCookie(cookie);
|
||||
|
||||
if (!nonce || hashStateGuid(nonce) !== stateValue) {
|
||||
if (!authContext?.authNonce || hashStateGuid(authContext.authNonce) !== stateValue) {
|
||||
context.res = response({
|
||||
context,
|
||||
status: 401,
|
||||
|
@ -401,7 +401,7 @@ const httpTrigger = async function (context: Context, request: http.IncomingMess
|
|||
return;
|
||||
}
|
||||
|
||||
if (isNonceExpired(nonce)) {
|
||||
if (isNonceExpired(authContext.authNonce)) {
|
||||
context.res = response({
|
||||
context,
|
||||
status: 401,
|
||||
|
@ -473,7 +473,7 @@ const httpTrigger = async function (context: Context, request: http.IncomingMess
|
|||
context,
|
||||
cookies: [
|
||||
{
|
||||
name: "Nonce",
|
||||
name: "StaticWebAppsAuthContextCookie",
|
||||
value: "deleted",
|
||||
path: "/",
|
||||
secure: true,
|
||||
|
@ -493,7 +493,7 @@ const httpTrigger = async function (context: Context, request: http.IncomingMess
|
|||
status: 302,
|
||||
headers: {
|
||||
status: 302,
|
||||
Location: `${SWA_CLI_APP_PROTOCOL}://${DEFAULT_CONFIG.host}:${DEFAULT_CONFIG.port}`,
|
||||
Location: authContext.postLoginRedirectUri ?? "/",
|
||||
},
|
||||
body: "",
|
||||
});
|
||||
|
|
|
@ -2,9 +2,9 @@ import { response } from "../../../core";
|
|||
import * as http from "http";
|
||||
import { SWA_CLI_APP_PROTOCOL } from "../../../core/constants";
|
||||
import { DEFAULT_CONFIG } from "../../../config";
|
||||
import { hashStateGuid, newNonceWithExpiration } from "../../../core/utils/auth";
|
||||
import { extractPostLoginRedirectUri, hashStateGuid, newNonceWithExpiration } from "../../../core/utils/auth";
|
||||
|
||||
const httpTrigger = async function (context: Context, _request: http.IncomingMessage, customAuth?: SWAConfigFileAuth) {
|
||||
const httpTrigger = async function (context: Context, request: http.IncomingMessage, customAuth?: SWAConfigFileAuth) {
|
||||
await Promise.resolve();
|
||||
|
||||
const providerName = context.bindingData?.provider?.toLowerCase() || "";
|
||||
|
@ -44,6 +44,12 @@ const httpTrigger = async function (context: Context, _request: http.IncomingMes
|
|||
}
|
||||
|
||||
const state = newNonceWithExpiration();
|
||||
|
||||
const authContext: AuthContext = {
|
||||
authNonce: state,
|
||||
postLoginRedirectUri: extractPostLoginRedirectUri(SWA_CLI_APP_PROTOCOL, request.headers.host, request.url),
|
||||
};
|
||||
|
||||
const hashedState = hashStateGuid(state);
|
||||
const redirectUri = `${SWA_CLI_APP_PROTOCOL}://${DEFAULT_CONFIG.host}:${DEFAULT_CONFIG.port}`;
|
||||
|
||||
|
@ -56,8 +62,8 @@ const httpTrigger = async function (context: Context, _request: http.IncomingMes
|
|||
context,
|
||||
cookies: [
|
||||
{
|
||||
name: "Nonce",
|
||||
value: btoa(state),
|
||||
name: "StaticWebAppsAuthContextCookie",
|
||||
value: btoa(JSON.stringify(authContext)),
|
||||
domain: DEFAULT_CONFIG.host,
|
||||
path: "/",
|
||||
secure: true,
|
||||
|
|
|
@ -240,6 +240,12 @@ declare type SWACLIConfigInfo = {
|
|||
declare type ResponseOptions = {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
declare type AuthContext = {
|
||||
authNonce: string;
|
||||
postLoginRedirectUri?: string;
|
||||
};
|
||||
|
||||
declare type ClientPrincipal = {
|
||||
identityProvider: string;
|
||||
userId: string;
|
||||
|
|
Загрузка…
Ссылка в новой задаче