New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More

fastify-openid-auth

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fastify-openid-auth - npm Package Compare versions

Comparing version

to
3.0.0

@@ -0,1 +1,20 @@

# [3.0.0](https://github.com/mikaelkaron/fastify-openid-auth/compare/v2.1.0...v3.0.0) (2022-03-01)
### Code Refactoring
* simplify login code ([5b5e1f8](https://github.com/mikaelkaron/fastify-openid-auth/commit/5b5e1f8502382b362efab04e74b24e05963b2c23))
### Features
* remove dynamic factories ([b30653b](https://github.com/mikaelkaron/fastify-openid-auth/commit/b30653b00d2c33745fc8f51201711575f99310ee))
* use `fastify-error` for plugin errors ([df1ccf9](https://github.com/mikaelkaron/fastify-openid-auth/commit/df1ccf930a6be499105d98d40236c2854da31b6a))
### BREAKING CHANGES
* `OpenIDLoginHandlerOptions.params` is now `OpenIDLoginHandlerOptions.parameters`
* Removed outer factories, resulting in `handlerFactory() => handler` is now just `handler`.
# [2.1.0](https://github.com/mikaelkaron/fastify-openid-auth/compare/v2.0.0...v2.1.0) (2022-02-22)

@@ -2,0 +21,0 @@

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

import { RouteHandlerMethod } from 'fastify';
import createError from 'fastify-error';
import { AuthorizationParameters, CallbackExtras, Client } from 'openid-client';

@@ -7,9 +8,9 @@ import { OpenIDWriteTokens } from './types';

session: {
get(key: string): any;
set(key: string, value: any): void;
get: <T>(key: string) => T;
set: (key: string, value: unknown) => void;
};
}
}
export interface OpenIDLoginOptions {
params?: AuthorizationParameters;
export interface OpenIDLoginHandlerOptions {
parameters?: AuthorizationParameters;
extras?: CallbackExtras;

@@ -20,3 +21,5 @@ usePKCE?: boolean | 'plain' | 'S256';

}
export declare type OpenIDLoginHandlerFactory = (options?: OpenIDLoginOptions) => RouteHandlerMethod;
export declare const openIDAuthLoginFactory: (client: Client, defaults?: OpenIDLoginOptions | undefined) => OpenIDLoginHandlerFactory;
export declare const SessionKeyError: createError.FastifyErrorConstructor;
export declare const SessionValueError: createError.FastifyErrorConstructor;
export declare const SupportedMethodError: createError.FastifyErrorConstructor;
export declare const openIDLoginHandlerFactory: (client: Client, options?: OpenIDLoginHandlerOptions | undefined) => RouteHandlerMethod;
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.openIDAuthLoginFactory = void 0;
exports.openIDLoginHandlerFactory = exports.SupportedMethodError = exports.SessionValueError = exports.SessionKeyError = void 0;
const fastify_error_1 = __importDefault(require("fastify-error"));
const openid_client_1 = require("openid-client");
const util_1 = require("util");
const pick = (obj, ...keys) => {
const ret = {};
keys.forEach((key) => {
ret[key] = obj[key];
});
return ret;
};
exports.SessionKeyError = (0, fastify_error_1.default)('FST_SESSION_KEY', 'client must have an issuer with an identifier', 500);
exports.SessionValueError = (0, fastify_error_1.default)('FST_SESSION_VALUE', 'did not find expected authorization request details in req.session["%s"]', 500);
exports.SupportedMethodError = (0, fastify_error_1.default)('FST_SUPPORTED_METHOD', 'neither code_challenge_method supported by the client is supported by the issuer', 500);
const resolveResponseType = (client) => {

@@ -27,3 +26,3 @@ const { length, 0: value } = client.metadata.response_types ?? [];

};
const resolveSupportedMethods = (issuer) => {
const resolveSupportedMethod = (issuer) => {
const supportedMethods = Array.isArray(issuer.code_challenge_methods_supported)

@@ -39,3 +38,3 @@ ? issuer.code_challenge_methods_supported

else {
throw new TypeError('neither code_challenge_method supported by the client is supported by the issuer');
throw new exports.SupportedMethodError();
}

@@ -45,82 +44,68 @@ };

if (issuer.metadata.issuer === undefined) {
throw new TypeError('client must have an issuer with an identifier');
throw new exports.SessionKeyError();
}
return `oidc:${new URL(issuer.metadata.issuer).hostname}`;
};
const openIDAuthLoginFactory = (client, defaults) => {
const _params = {
scope: 'openid',
response_type: resolveResponseType(client),
redirect_uri: resolveRedirectUri(client),
...defaults?.params,
};
const _sessionKey = defaults?.sessionKey !== undefined
? defaults.sessionKey
const openIDLoginHandlerFactory = (client, options) => {
const redirect_uri = options?.parameters?.redirect_uri !== undefined
? options.parameters.redirect_uri
: resolveRedirectUri(client);
const sessionKey = options?.sessionKey !== undefined
? options.sessionKey
: resolveSessionKey(client.issuer);
const _usePKCE = defaults?.usePKCE !== undefined
? defaults.usePKCE === true
? resolveSupportedMethods(client.issuer)
: defaults.usePKCE
const usePKCE = options?.usePKCE !== undefined
? options.usePKCE === true
? resolveSupportedMethod(client.issuer)
: options.usePKCE
: false;
const openIDLoginHandlerFactory = (options) => {
const { sessionKey = _sessionKey, usePKCE = _usePKCE, write, } = { ...defaults, ...options };
return async function openIDLoginHandler(request, reply) {
const parameters = client.callbackParams(request.raw);
// #region authentication request
if (Object.keys(parameters).length === 0) {
const params = {
state: openid_client_1.generators.random(),
..._params,
...options?.params,
};
if (params.nonce === undefined && params.response_type === 'code') {
params.nonce = openid_client_1.generators.random();
const { write } = { ...options };
return async function openIDLoginHandler(request, reply) {
const callbackParams = client.callbackParams(request.raw);
// #region authentication request
if (Object.keys(callbackParams).length === 0) {
const response_type = options?.parameters?.response_type !== undefined
? options.parameters.response_type
: resolveResponseType(client);
const parameters = {
scope: 'openid',
state: openid_client_1.generators.random(),
redirect_uri,
response_type,
...options?.parameters,
};
if (parameters.nonce === undefined &&
parameters.response_type === 'code') {
parameters.nonce = openid_client_1.generators.random();
}
const callbackChecks = (({ nonce, state, max_age, response_type, }) => ({ nonce, state, max_age, response_type }))(parameters);
if (usePKCE !== false && parameters.response_type === 'code') {
const verifier = openid_client_1.generators.random();
callbackChecks.code_verifier = verifier;
switch (usePKCE) {
case 'S256':
parameters.code_challenge = openid_client_1.generators.codeChallenge(verifier);
parameters.code_challenge_method = 'S256';
break;
case 'plain':
parameters.code_challenge = verifier;
break;
}
const sessionValue = pick(params, 'nonce', 'state', 'max_age', 'response_type');
if (usePKCE !== false && params.response_type === 'code') {
const verifier = openid_client_1.generators.random();
sessionValue.code_verifier = verifier;
switch (usePKCE) {
case 'S256':
params.code_challenge = openid_client_1.generators.codeChallenge(verifier);
params.code_challenge_method = 'S256';
break;
case 'plain':
params.code_challenge = verifier;
break;
}
}
request.session.set(sessionKey, sessionValue);
return await reply.redirect(client.authorizationUrl(params));
}
// #endregion
// #region authentication response
const sessionValue = request.session.get(sessionKey);
if (sessionValue === undefined ||
Object.keys(sessionValue).length === 0) {
throw new Error((0, util_1.format)('did not find expected authorization request details in session, req.session["%s"] is %j', sessionKey, sessionValue));
}
request.session.set(sessionKey, undefined);
const params = {
..._params,
...options?.params,
};
const extras = { ...defaults?.extras, ...options?.extras };
// eslint-disable-next-line @typescript-eslint/naming-convention
const { state, nonce, max_age, code_verifier, response_type } = sessionValue;
const checks = {
state,
nonce,
max_age,
code_verifier,
response_type,
};
const tokenset = await client.callback(params.redirect_uri, parameters, checks, extras);
return await write?.call(this, request, reply, tokenset);
// #endregion
};
request.session.set(sessionKey, callbackChecks);
return await reply.redirect(client.authorizationUrl(parameters));
}
// #endregion
// #region authentication response
const callbackChecks = request.session.get(sessionKey);
if (callbackChecks === undefined ||
Object.keys(callbackChecks).length === 0) {
throw new exports.SessionValueError(sessionKey);
}
request.session.set(sessionKey, undefined);
const tokenset = await client.callback(redirect_uri, callbackParams, callbackChecks, options?.extras);
return await write?.call(this, request, reply, tokenset);
// #endregion
};
return openIDLoginHandlerFactory;
};
exports.openIDAuthLoginFactory = openIDAuthLoginFactory;
exports.openIDLoginHandlerFactory = openIDLoginHandlerFactory;
//# sourceMappingURL=login.js.map
import { RouteHandlerMethod } from 'fastify';
import { Client, EndSessionParameters } from 'openid-client';
import { OpenIDReadTokens, OpenIDWriteTokens } from './types';
export interface OpenIDLogoutOptions {
export interface OpenIDLogoutHandlerOptions {
parameters?: EndSessionParameters;

@@ -9,3 +9,2 @@ read: OpenIDReadTokens;

}
export declare type OpenIDLogoutHandlerFactory = (options?: OpenIDLogoutOptions) => RouteHandlerMethod;
export declare const openIDAuthLogoutFactory: (client: Client, defaults: OpenIDLogoutOptions) => OpenIDLogoutHandlerFactory;
export declare const openIDLogoutHandlerFactory: (client: Client, options: OpenIDLogoutHandlerOptions) => RouteHandlerMethod;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.openIDAuthLogoutFactory = void 0;
const openIDAuthLogoutFactory = (client, defaults) => {
const openIDLogoutHandlerFactory = (options) => {
const { parameters, read, write } = { ...defaults, ...options };
return async function openIDLogoutHandler(request, reply) {
const tokenset = await read.call(this, request, reply);
// #region authentication request
if (Object.keys(request.query).length === 0) {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { id_token, session_state } = tokenset;
if (id_token !== undefined) {
return await reply.redirect(client.endSessionUrl({
id_token_hint: id_token,
state: session_state,
...parameters,
}));
}
exports.openIDLogoutHandlerFactory = void 0;
const openIDLogoutHandlerFactory = (client, options) => {
const { parameters, read, write } = options;
return async function openIDLogoutHandler(request, reply) {
const tokenset = await read.call(this, request, reply);
// #region authentication request
if (Object.keys(request.query).length === 0) {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { id_token, session_state } = tokenset;
if (id_token !== undefined) {
return await reply.redirect(client.endSessionUrl({
id_token_hint: id_token,
state: session_state,
...parameters,
}));
}
// #endregion
// #region authentication response
return await write?.call(this, request, reply, tokenset);
// #endregion
};
}
// #endregion
// #region authentication response
return await write?.call(this, request, reply, tokenset);
// #endregion
};
return openIDLogoutHandlerFactory;
};
exports.openIDAuthLogoutFactory = openIDAuthLogoutFactory;
exports.openIDLogoutHandlerFactory = openIDLogoutHandlerFactory;
//# sourceMappingURL=logout.js.map

@@ -1,21 +0,21 @@

import { FastifyPluginAsync } from 'fastify';
import { FastifyPluginAsync, RouteHandlerMethod } from 'fastify';
import { Client } from 'openid-client';
import { OpenIDLoginHandlerFactory, OpenIDLoginOptions } from './login';
import { OpenIDLogoutHandlerFactory, OpenIDLogoutOptions } from './logout';
import { OpenIDRefreshHandlerFactory, OpenIDRefreshOptions } from './refresh';
import { OpenIDVerifyHandlerFactory, OpenIDVerifyOptions } from './verify';
import { OpenIDLoginHandlerOptions } from './login';
import { OpenIDLogoutHandlerOptions } from './logout';
import { OpenIDRefreshHandlerOptions } from './refresh';
import { OpenIDVerifyHandlerOptions } from './verify';
export interface FastifyOpenIDAuthPluginOptions {
name: string;
client: Client;
login?: OpenIDLoginOptions;
verify: OpenIDVerifyOptions;
refresh: OpenIDRefreshOptions;
logout: OpenIDLogoutOptions;
login?: OpenIDLoginHandlerOptions;
verify: OpenIDVerifyHandlerOptions;
refresh: OpenIDRefreshHandlerOptions;
logout: OpenIDLogoutHandlerOptions;
}
export interface OpenIDAuthNamespace {
login: OpenIDLoginHandlerFactory;
verify: OpenIDVerifyHandlerFactory;
refresh: OpenIDRefreshHandlerFactory;
logout: OpenIDLogoutHandlerFactory;
login: RouteHandlerMethod;
verify: RouteHandlerMethod;
refresh: RouteHandlerMethod;
logout: RouteHandlerMethod;
}
export declare const openIDAuthPlugin: FastifyPluginAsync<FastifyOpenIDAuthPluginOptions>;

@@ -15,6 +15,6 @@ "use strict";

const openIDAuthNamespace = {
login: (0, login_1.openIDAuthLoginFactory)(client, login),
refresh: (0, refresh_1.openIDAuthRefreshFactory)(client, refresh),
verify: (0, verify_1.openIDAuthVerifyFactory)(verify),
logout: (0, logout_1.openIDAuthLogoutFactory)(client, logout),
login: (0, login_1.openIDLoginHandlerFactory)(client, login),
refresh: (0, refresh_1.openIDRefreshHandlerFactory)(client, refresh),
verify: (0, verify_1.openIDVerifyHandlerFactory)(verify),
logout: (0, logout_1.openIDLogoutHandlerFactory)(client, logout),
};

@@ -21,0 +21,0 @@ fastify.log.trace(`decorating \`fastify[${name}]\` with OpenIDAuthNamespace`);

import { RouteHandlerMethod } from 'fastify';
import { Client, RefreshExtras } from 'openid-client';
import { OpenIDReadTokens, OpenIDWriteTokens } from './types';
export interface OpenIDRefreshOptions {
export interface OpenIDRefreshHandlerOptions {
extras?: RefreshExtras;

@@ -9,3 +9,2 @@ read: OpenIDReadTokens;

}
export declare type OpenIDRefreshHandlerFactory = (options?: OpenIDRefreshOptions) => RouteHandlerMethod;
export declare const openIDAuthRefreshFactory: (client: Client, defaults: OpenIDRefreshOptions) => OpenIDRefreshHandlerFactory;
export declare const openIDRefreshHandlerFactory: (client: Client, options: OpenIDRefreshHandlerOptions) => RouteHandlerMethod;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.openIDAuthRefreshFactory = void 0;
const openIDAuthRefreshFactory = (client, defaults) => {
const openIDRefreshHandlerFactory = (options) => {
const { extras, read, write } = { ...defaults, ...options };
return async function openIDRefreshHandler(request, reply) {
const oldTokenset = await read.call(this, request, reply);
if (oldTokenset.expired()) {
request.log.trace(`OpenID token expired ${oldTokenset.expires_at !== undefined
? new Date(oldTokenset.expires_at * 1000).toUTCString()
: 'recently'}, refreshing`);
const newTokenset = await client.refresh(oldTokenset, extras);
request.log.trace('OpenID tokens refreshed');
return await write?.call(this, request, reply, newTokenset);
}
};
exports.openIDRefreshHandlerFactory = void 0;
const openIDRefreshHandlerFactory = (client, options) => {
const { extras, read, write } = options;
return async function openIDRefreshHandler(request, reply) {
const oldTokenset = await read.call(this, request, reply);
if (oldTokenset.expired()) {
request.log.trace(`OpenID token expired ${oldTokenset.expires_at !== undefined
? new Date(oldTokenset.expires_at * 1000).toUTCString()
: 'recently'}, refreshing`);
const newTokenset = await client.refresh(oldTokenset, extras);
request.log.trace('OpenID tokens refreshed');
return await write?.call(this, request, reply, newTokenset);
}
};
return openIDRefreshHandlerFactory;
};
exports.openIDAuthRefreshFactory = openIDAuthRefreshFactory;
exports.openIDRefreshHandlerFactory = openIDRefreshHandlerFactory;
//# sourceMappingURL=refresh.js.map

@@ -6,3 +6,3 @@ import { RouteHandlerMethod } from 'fastify';

export declare type OpenIDVerifyTokens = keyof Pick<TokenSet, 'id_token' | 'access_token' | 'refresh_token'>;
export interface OpenIDVerifyOptions {
export interface OpenIDVerifyHandlerOptions {
options?: JWTVerifyOptions;

@@ -14,3 +14,2 @@ key: JWTVerifyGetKey | KeyLike | Uint8Array;

}
export declare type OpenIDVerifyHandlerFactory = (options?: OpenIDVerifyOptions) => RouteHandlerMethod;
export declare const openIDAuthVerifyFactory: (defaults: OpenIDVerifyOptions) => OpenIDVerifyHandlerFactory;
export declare const openIDVerifyHandlerFactory: (options: OpenIDVerifyHandlerOptions) => RouteHandlerMethod;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.openIDAuthVerifyFactory = void 0;
exports.openIDVerifyHandlerFactory = void 0;
const jose_1 = require("jose");
const openIDAuthVerifyFactory = (defaults) => {
const openIDVerifyHandlerFactory = (options) => {
const { options: jwtVerifyOptions, key, tokens = ['id_token'], read, write, } = { ...defaults, ...options };
return async function openIDVerify(request, reply) {
const tokenset = await read.call(this, request, reply);
// eslint-disable-next-line @typescript-eslint/naming-convention
for (const token of tokens) {
const jwt = tokenset[token];
if (jwt !== undefined) {
key instanceof Function
? await (0, jose_1.jwtVerify)(jwt, key, jwtVerifyOptions)
: await (0, jose_1.jwtVerify)(jwt, key, jwtVerifyOptions);
}
const openIDVerifyHandlerFactory = (options) => {
const { options: jwtVerifyOptions, key, tokens = ['id_token'], read, write, } = options;
return async function openIDVerify(request, reply) {
const tokenset = await read.call(this, request, reply);
// eslint-disable-next-line @typescript-eslint/naming-convention
for (const token of tokens) {
const jwt = tokenset[token];
if (jwt !== undefined) {
key instanceof Function
? await (0, jose_1.jwtVerify)(jwt, key, jwtVerifyOptions)
: await (0, jose_1.jwtVerify)(jwt, key, jwtVerifyOptions);
}
return await write?.call(this, request, reply, tokenset);
};
}
return await write?.call(this, request, reply, tokenset);
};
return openIDVerifyHandlerFactory;
};
exports.openIDAuthVerifyFactory = openIDAuthVerifyFactory;
exports.openIDVerifyHandlerFactory = openIDVerifyHandlerFactory;
//# sourceMappingURL=verify.js.map
{
"name": "fastify-openid-auth",
"version": "2.1.0",
"version": "3.0.0",
"description": "Fastify auth plugin for openid-client",

@@ -33,17 +33,18 @@ "main": "dist/index.js",

"@semantic-release/github": "^8.0.2",
"@semantic-release/npm": "^9.0.0",
"@semantic-release/npm": "^9.0.1",
"@semantic-release/release-notes-generator": "^10.0.3",
"@tsconfig/node16": "^1.0.2",
"@types/node": "^17.0.17",
"@types/node": "^17.0.21",
"@wtf/eslint-config": "^1.0.0",
"semantic-release": "^19.0.2",
"shx": "^0.3.4",
"typescript": "^4.5.5"
"typescript": "^4.6.2"
},
"dependencies": {
"fastify": "^3.27.1",
"fastify": "^3.27.2",
"fastify-error": "^1.0.0",
"fastify-plugin": "^3.0.1",
"jose": "^4.5.0",
"jose": "^4.5.1",
"openid-client": "^5.1.3"
}
}

@@ -1,3 +0,4 @@

/* eslint-disable @typescript-eslint/method-signature-style */
/* eslint-disable @typescript-eslint/naming-convention */
import { RouteHandlerMethod } from 'fastify';
import createError from 'fastify-error';
import {

@@ -11,3 +12,2 @@ AuthorizationParameters,

} from 'openid-client';
import { format } from 'util';
import { OpenIDWriteTokens } from './types';

@@ -18,9 +18,10 @@

session: {
get(key: string): any;
set(key: string, value: any): void;
get: <T>(key: string) => T;
set: (key: string, value: unknown) => void;
};
}
}
export interface OpenIDLoginOptions {
params?: AuthorizationParameters;
export interface OpenIDLoginHandlerOptions {
parameters?: AuthorizationParameters;
extras?: CallbackExtras;

@@ -32,14 +33,20 @@ usePKCE?: boolean | 'plain' | 'S256';

export type OpenIDLoginHandlerFactory = (
options?: OpenIDLoginOptions
) => RouteHandlerMethod;
export const SessionKeyError = createError(
'FST_SESSION_KEY',
'client must have an issuer with an identifier',
500
);
const pick = <T, K extends keyof T>(obj: T, ...keys: K[]): Pick<T, K> => {
const ret: any = {};
keys.forEach((key) => {
ret[key] = obj[key];
});
return ret;
};
export const SessionValueError = createError(
'FST_SESSION_VALUE',
'did not find expected authorization request details in req.session["%s"]',
500
);
export const SupportedMethodError = createError(
'FST_SUPPORTED_METHOD',
'neither code_challenge_method supported by the client is supported by the issuer',
500
);
const resolveResponseType = (client: Client): string | undefined => {

@@ -65,3 +72,3 @@ const { length, 0: value } = client.metadata.response_types ?? [];

const resolveSupportedMethods = (issuer: Issuer): string => {
const resolveSupportedMethod = (issuer: Issuer): string => {
const supportedMethods = Array.isArray(

@@ -78,5 +85,3 @@ issuer.code_challenge_methods_supported

} else {
throw new TypeError(
'neither code_challenge_method supported by the client is supported by the issuer'
);
throw new SupportedMethodError();
}

@@ -87,3 +92,3 @@ };

if (issuer.metadata.issuer === undefined) {
throw new TypeError('client must have an issuer with an identifier');
throw new SessionKeyError();
}

@@ -93,116 +98,94 @@ return `oidc:${new URL(issuer.metadata.issuer).hostname}`;

export const openIDAuthLoginFactory = (
export const openIDLoginHandlerFactory = (
client: Client,
defaults?: OpenIDLoginOptions
): OpenIDLoginHandlerFactory => {
const _params: AuthorizationParameters = {
scope: 'openid',
response_type: resolveResponseType(client),
redirect_uri: resolveRedirectUri(client),
...defaults?.params,
};
const _sessionKey =
defaults?.sessionKey !== undefined
? defaults.sessionKey
options?: OpenIDLoginHandlerOptions
): RouteHandlerMethod => {
const redirect_uri =
options?.parameters?.redirect_uri !== undefined
? options.parameters.redirect_uri
: resolveRedirectUri(client);
const sessionKey =
options?.sessionKey !== undefined
? options.sessionKey
: resolveSessionKey(client.issuer);
const _usePKCE =
defaults?.usePKCE !== undefined
? defaults.usePKCE === true
? resolveSupportedMethods(client.issuer)
: defaults.usePKCE
const usePKCE =
options?.usePKCE !== undefined
? options.usePKCE === true
? resolveSupportedMethod(client.issuer)
: options.usePKCE
: false;
const openIDLoginHandlerFactory: OpenIDLoginHandlerFactory = (options?) => {
const {
sessionKey = _sessionKey,
usePKCE = _usePKCE,
write,
} = { ...defaults, ...options };
const { write } = { ...options };
return async function openIDLoginHandler(request, reply) {
const parameters = client.callbackParams(request.raw);
return async function openIDLoginHandler(request, reply) {
const callbackParams = client.callbackParams(request.raw);
// #region authentication request
if (Object.keys(parameters).length === 0) {
const params = {
state: generators.random(),
..._params,
...options?.params,
};
if (params.nonce === undefined && params.response_type === 'code') {
params.nonce = generators.random();
}
const sessionValue: Record<string, unknown> = pick(
params,
'nonce',
'state',
'max_age',
'response_type'
);
if (usePKCE !== false && params.response_type === 'code') {
const verifier = generators.random();
// #region authentication request
if (Object.keys(callbackParams).length === 0) {
const response_type =
options?.parameters?.response_type !== undefined
? options.parameters.response_type
: resolveResponseType(client);
const parameters = {
scope: 'openid',
state: generators.random(),
redirect_uri,
response_type,
...options?.parameters,
};
if (
parameters.nonce === undefined &&
parameters.response_type === 'code'
) {
parameters.nonce = generators.random();
}
const callbackChecks: OpenIDCallbackChecks = (({
nonce,
state,
max_age,
response_type,
}) => ({ nonce, state, max_age, response_type }))(parameters);
if (usePKCE !== false && parameters.response_type === 'code') {
const verifier = generators.random();
sessionValue.code_verifier = verifier;
callbackChecks.code_verifier = verifier;
switch (usePKCE) {
case 'S256':
params.code_challenge = generators.codeChallenge(verifier);
params.code_challenge_method = 'S256';
break;
case 'plain':
params.code_challenge = verifier;
break;
}
switch (usePKCE) {
case 'S256':
parameters.code_challenge = generators.codeChallenge(verifier);
parameters.code_challenge_method = 'S256';
break;
case 'plain':
parameters.code_challenge = verifier;
break;
}
}
request.session.set(sessionKey, sessionValue);
request.session.set(sessionKey, callbackChecks);
return await reply.redirect(client.authorizationUrl(params));
}
// #endregion
return await reply.redirect(client.authorizationUrl(parameters));
}
// #endregion
// #region authentication response
const sessionValue = request.session.get(sessionKey);
if (
sessionValue === undefined ||
Object.keys(sessionValue).length === 0
) {
throw new Error(
format(
'did not find expected authorization request details in session, req.session["%s"] is %j',
sessionKey,
sessionValue
)
);
}
// #region authentication response
const callbackChecks: OpenIDCallbackChecks =
request.session.get(sessionKey);
if (
callbackChecks === undefined ||
Object.keys(callbackChecks).length === 0
) {
throw new SessionValueError(sessionKey);
}
request.session.set(sessionKey, undefined);
request.session.set(sessionKey, undefined);
const params = {
..._params,
...options?.params,
};
const extras = { ...defaults?.extras, ...options?.extras };
// eslint-disable-next-line @typescript-eslint/naming-convention
const { state, nonce, max_age, code_verifier, response_type } =
sessionValue;
const checks: OpenIDCallbackChecks = {
state,
nonce,
max_age,
code_verifier,
response_type,
};
const tokenset = await client.callback(
params.redirect_uri,
parameters,
checks,
extras
);
return await write?.call(this, request, reply, tokenset);
// #endregion
};
const tokenset = await client.callback(
redirect_uri,
callbackParams,
callbackChecks,
options?.extras
);
return await write?.call(this, request, reply, tokenset);
// #endregion
};
return openIDLoginHandlerFactory;
};

@@ -5,3 +5,3 @@ import { RouteHandlerMethod } from 'fastify';

export interface OpenIDLogoutOptions {
export interface OpenIDLogoutHandlerOptions {
parameters?: EndSessionParameters;

@@ -12,39 +12,31 @@ read: OpenIDReadTokens;

export type OpenIDLogoutHandlerFactory = (
options?: OpenIDLogoutOptions
) => RouteHandlerMethod;
export const openIDAuthLogoutFactory = (
export const openIDLogoutHandlerFactory = (
client: Client,
defaults: OpenIDLogoutOptions
): OpenIDLogoutHandlerFactory => {
const openIDLogoutHandlerFactory: OpenIDLogoutHandlerFactory = (options?) => {
const { parameters, read, write } = { ...defaults, ...options };
options: OpenIDLogoutHandlerOptions
): RouteHandlerMethod => {
const { parameters, read, write } = options;
return async function openIDLogoutHandler(request, reply) {
const tokenset = await read.call(this, request, reply);
return async function openIDLogoutHandler(request, reply) {
const tokenset = await read.call(this, request, reply);
// #region authentication request
if (Object.keys(request.query as object).length === 0) {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { id_token, session_state } = tokenset;
if (id_token !== undefined) {
return await reply.redirect(
client.endSessionUrl({
id_token_hint: id_token,
state: session_state,
...parameters,
})
);
}
// #region authentication request
if (Object.keys(request.query as object).length === 0) {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { id_token, session_state } = tokenset;
if (id_token !== undefined) {
return await reply.redirect(
client.endSessionUrl({
id_token_hint: id_token,
state: session_state,
...parameters,
})
);
}
// #endregion
}
// #endregion
// #region authentication response
return await write?.call(this, request, reply, tokenset);
// #endregion
};
// #region authentication response
return await write?.call(this, request, reply, tokenset);
// #endregion
};
return openIDLogoutHandlerFactory;
};

@@ -1,23 +0,16 @@

import { FastifyPluginAsync } from 'fastify';
import { FastifyPluginAsync, RouteHandlerMethod } from 'fastify';
import fp from 'fastify-plugin';
import { Client } from 'openid-client';
import { openIDLoginHandlerFactory, OpenIDLoginHandlerOptions } from './login';
import {
openIDAuthLoginFactory,
OpenIDLoginHandlerFactory,
OpenIDLoginOptions,
} from './login';
import {
openIDAuthLogoutFactory,
OpenIDLogoutHandlerFactory,
OpenIDLogoutOptions,
openIDLogoutHandlerFactory,
OpenIDLogoutHandlerOptions,
} from './logout';
import {
openIDAuthRefreshFactory,
OpenIDRefreshHandlerFactory,
OpenIDRefreshOptions,
openIDRefreshHandlerFactory,
OpenIDRefreshHandlerOptions,
} from './refresh';
import {
openIDAuthVerifyFactory,
OpenIDVerifyHandlerFactory,
OpenIDVerifyOptions,
openIDVerifyHandlerFactory,
OpenIDVerifyHandlerOptions,
} from './verify';

@@ -28,13 +21,13 @@

client: Client;
login?: OpenIDLoginOptions;
verify: OpenIDVerifyOptions;
refresh: OpenIDRefreshOptions;
logout: OpenIDLogoutOptions;
login?: OpenIDLoginHandlerOptions;
verify: OpenIDVerifyHandlerOptions;
refresh: OpenIDRefreshHandlerOptions;
logout: OpenIDLogoutHandlerOptions;
}
export interface OpenIDAuthNamespace {
login: OpenIDLoginHandlerFactory;
verify: OpenIDVerifyHandlerFactory;
refresh: OpenIDRefreshHandlerFactory;
logout: OpenIDLogoutHandlerFactory;
login: RouteHandlerMethod;
verify: RouteHandlerMethod;
refresh: RouteHandlerMethod;
logout: RouteHandlerMethod;
}

@@ -48,6 +41,6 @@

const openIDAuthNamespace: OpenIDAuthNamespace = {
login: openIDAuthLoginFactory(client, login),
refresh: openIDAuthRefreshFactory(client, refresh),
verify: openIDAuthVerifyFactory(verify),
logout: openIDAuthLogoutFactory(client, logout),
login: openIDLoginHandlerFactory(client, login),
refresh: openIDRefreshHandlerFactory(client, refresh),
verify: openIDVerifyHandlerFactory(verify),
logout: openIDLogoutHandlerFactory(client, logout),
};

@@ -54,0 +47,0 @@

@@ -5,3 +5,3 @@ import { RouteHandlerMethod } from 'fastify';

export interface OpenIDRefreshOptions {
export interface OpenIDRefreshHandlerOptions {
extras?: RefreshExtras;

@@ -12,33 +12,23 @@ read: OpenIDReadTokens;

export type OpenIDRefreshHandlerFactory = (
options?: OpenIDRefreshOptions
) => RouteHandlerMethod;
export const openIDAuthRefreshFactory = (
export const openIDRefreshHandlerFactory = (
client: Client,
defaults: OpenIDRefreshOptions
): OpenIDRefreshHandlerFactory => {
const openIDRefreshHandlerFactory: OpenIDRefreshHandlerFactory = (
options?
) => {
const { extras, read, write } = { ...defaults, ...options };
options: OpenIDRefreshHandlerOptions
): RouteHandlerMethod => {
const { extras, read, write } = options;
return async function openIDRefreshHandler(request, reply) {
const oldTokenset = await read.call(this, request, reply);
if (oldTokenset.expired()) {
request.log.trace(
`OpenID token expired ${
oldTokenset.expires_at !== undefined
? new Date(oldTokenset.expires_at * 1000).toUTCString()
: 'recently'
}, refreshing`
);
const newTokenset = await client.refresh(oldTokenset, extras);
request.log.trace('OpenID tokens refreshed');
return await write?.call(this, request, reply, newTokenset);
}
};
return async function openIDRefreshHandler(request, reply) {
const oldTokenset = await read.call(this, request, reply);
if (oldTokenset.expired()) {
request.log.trace(
`OpenID token expired ${
oldTokenset.expires_at !== undefined
? new Date(oldTokenset.expires_at * 1000).toUTCString()
: 'recently'
}, refreshing`
);
const newTokenset = await client.refresh(oldTokenset, extras);
request.log.trace('OpenID tokens refreshed');
return await write?.call(this, request, reply, newTokenset);
}
};
return openIDRefreshHandlerFactory;
};

@@ -11,3 +11,3 @@ import { RouteHandlerMethod } from 'fastify';

export interface OpenIDVerifyOptions {
export interface OpenIDVerifyHandlerOptions {
options?: JWTVerifyOptions;

@@ -20,34 +20,26 @@ key: JWTVerifyGetKey | KeyLike | Uint8Array;

export type OpenIDVerifyHandlerFactory = (
options?: OpenIDVerifyOptions
) => RouteHandlerMethod;
export const openIDVerifyHandlerFactory = (
options: OpenIDVerifyHandlerOptions
): RouteHandlerMethod => {
const {
options: jwtVerifyOptions,
key,
tokens = ['id_token'],
read,
write,
} = options;
export const openIDAuthVerifyFactory = (
defaults: OpenIDVerifyOptions
): OpenIDVerifyHandlerFactory => {
const openIDVerifyHandlerFactory: OpenIDVerifyHandlerFactory = (options?) => {
const {
options: jwtVerifyOptions,
key,
tokens = ['id_token'],
read,
write,
} = { ...defaults, ...options };
return async function openIDVerify(request, reply) {
const tokenset = await read.call(this, request, reply);
// eslint-disable-next-line @typescript-eslint/naming-convention
for (const token of tokens) {
const jwt = tokenset[token];
if (jwt !== undefined) {
key instanceof Function
? await jwtVerify(jwt, key, jwtVerifyOptions)
: await jwtVerify(jwt, key, jwtVerifyOptions);
}
return async function openIDVerify(request, reply) {
const tokenset = await read.call(this, request, reply);
// eslint-disable-next-line @typescript-eslint/naming-convention
for (const token of tokens) {
const jwt = tokenset[token];
if (jwt !== undefined) {
key instanceof Function
? await jwtVerify(jwt, key, jwtVerifyOptions)
: await jwtVerify(jwt, key, jwtVerifyOptions);
}
return await write?.call(this, request, reply, tokenset);
};
}
return await write?.call(this, request, reply, tokenset);
};
return openIDVerifyHandlerFactory;
};

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet