Skip to content

Commit

Permalink
feat(core): Allow cookie & bearer session tokens at the same time
Browse files Browse the repository at this point in the history
Closes #960
michaelbromley committed Jul 26, 2021

Verified

This commit was signed with the committer’s verified signature.
rouault Even Rouault
1 parent 64a3380 commit fc6b890
Showing 5 changed files with 53 additions and 17 deletions.
5 changes: 4 additions & 1 deletion packages/admin-ui-plugin/src/plugin.ts
Original file line number Diff line number Diff line change
@@ -212,7 +212,10 @@ export class AdminUiPlugin implements NestModule {
adminApiPath: propOrDefault('adminApiPath', this.configService.apiOptions.adminApiPath),
apiHost: propOrDefault('apiHost', 'auto'),
apiPort: propOrDefault('apiPort', 'auto'),
tokenMethod: propOrDefault('tokenMethod', authOptions.tokenMethod || 'cookie'),
tokenMethod: propOrDefault(
'tokenMethod',
authOptions.tokenMethod === 'bearer' ? 'bearer' : 'cookie',
),
authTokenHeaderKey: propOrDefault(
'authTokenHeaderKey',
authOptions.authTokenHeaderKey || DEFAULT_AUTH_TOKEN_HEADER_KEY,
39 changes: 28 additions & 11 deletions packages/core/src/api/common/extract-session-token.ts
Original file line number Diff line number Diff line change
@@ -8,19 +8,36 @@ import { AuthOptions } from '../../config/vendure-config';
*/
export function extractSessionToken(
req: Request,
tokenMethod: AuthOptions['tokenMethod'],
tokenMethod: Exclude<AuthOptions['tokenMethod'], undefined>,
): string | undefined {
const tokenFromCookie = getFromCookie(req);
const tokenFromHeader = getFromHeader(req);

if (tokenMethod === 'cookie') {
if (req.session && req.session.token) {
return req.session.token;
}
} else {
const authHeader = req.get('Authorization');
if (authHeader) {
const matches = authHeader.match(/bearer\s+(.+)$/i);
if (matches) {
return matches[1];
}
return tokenFromCookie;
} else if (tokenMethod === 'bearer') {
return tokenFromHeader;
}
if (tokenMethod.includes('cookie') && tokenFromCookie) {
return tokenFromCookie;
}
if (tokenMethod.includes('bearer') && tokenFromHeader) {
return tokenFromHeader;
}
}

function getFromCookie(req: Request): string | undefined {
if (req.session && req.session.token) {
return req.session.token;
}
}

function getFromHeader(req: Request): string | undefined {
const authHeader = req.get('Authorization');
if (authHeader) {
const matches = authHeader.match(/bearer\s+(.+)$/i);
if (matches) {
return matches[1];
}
}
}
12 changes: 10 additions & 2 deletions packages/core/src/api/common/set-session-token.ts
Original file line number Diff line number Diff line change
@@ -15,14 +15,22 @@ export function setSessionToken(options: {
res: Response;
}) {
const { sessionToken, rememberMe, authOptions, req, res } = options;
if (authOptions.tokenMethod === 'cookie') {
const usingCookie =
authOptions.tokenMethod === 'cookie' ||
(Array.isArray(authOptions.tokenMethod) && authOptions.tokenMethod.includes('cookie'));
const usingBearer =
authOptions.tokenMethod === 'bearer' ||
(Array.isArray(authOptions.tokenMethod) && authOptions.tokenMethod.includes('bearer'));

if (usingCookie) {
if (req.session) {
if (rememberMe) {
req.sessionOptions.maxAge = ms('1y');
}
req.session.token = sessionToken;
}
} else {
}
if (usingBearer) {
res.set(authOptions.authTokenHeaderKey, sessionToken);
}
}
10 changes: 8 additions & 2 deletions packages/core/src/bootstrap.ts
Original file line number Diff line number Diff line change
@@ -55,7 +55,10 @@ export async function bootstrap(userConfig: Partial<VendureConfig>): Promise<INe
});
DefaultLogger.restoreOriginalLogLevel();
app.useLogger(new Logger());
if (config.authOptions.tokenMethod === 'cookie') {
const { tokenMethod } = config.authOptions;
const usingCookie =
tokenMethod === 'cookie' || (Array.isArray(tokenMethod) && tokenMethod.includes('cookie'));
if (usingCookie) {
const { cookieOptions } = config.authOptions;
app.use(cookieSession(cookieOptions));
}
@@ -186,7 +189,10 @@ export async function getAllEntities(userConfig: Partial<VendureConfig>): Promis
* in the CORS options, making sure to preserve any user-configured exposedHeaders.
*/
function setExposedHeaders(config: Readonly<RuntimeVendureConfig>) {
if (config.authOptions.tokenMethod === 'bearer') {
const { tokenMethod } = config.authOptions;
const isUsingBearerToken =
tokenMethod === 'bearer' || (Array.isArray(tokenMethod) && tokenMethod.includes('bearer'));
if (isUsingBearerToken) {
const authTokenHeaderKey = config.authOptions.authTokenHeaderKey;
const corsOptions = config.apiOptions.cors;
if (typeof corsOptions !== 'boolean') {
4 changes: 3 additions & 1 deletion packages/core/src/config/vendure-config.ts
Original file line number Diff line number Diff line change
@@ -296,9 +296,11 @@ export interface AuthOptions {
* `authTokenHeaderKey` in the server's CORS configuration (adding `Access-Control-Expose-Headers: vendure-auth-token`
* by default).
*
* From v1.2.0 is is possible to specify both methods as a tuple: `['cookie', 'bearer']`.
*
* @default 'cookie'
*/
tokenMethod?: 'cookie' | 'bearer';
tokenMethod?: 'cookie' | 'bearer' | ReadonlyArray<'cookie' | 'bearer'>;
/**
* @description
* Options related to the handling of cookies when using the 'cookie' tokenMethod.

0 comments on commit fc6b890

Please sign in to comment.