@zeushq/nextjs-zidentity
Version:
Next.js SDK for signin in with Zeus Identity
166 lines (159 loc) • 5.89 kB
text/typescript
import Joi from 'joi';
import { getLoginState } from './hooks/get-login-state';
import { Config } from './config';
const isHttps = /^https:/i;
const paramsSchema = Joi.object({
secret: Joi.alternatives([
Joi.string().min(8),
Joi.binary().min(8),
Joi.array().items(Joi.string().min(8), Joi.binary().min(8))
]).required(),
session: Joi.object({
rolling: Joi.boolean().optional().default(true),
rollingDuration: Joi.when(Joi.ref('rolling'), {
is: true,
then: Joi.number().integer().messages({
'number.base': '"session.rollingDuration" must be provided an integer value when "session.rolling" is true'
}),
otherwise: Joi.boolean().valid(false).messages({
'any.only': '"session.rollingDuration" must be false when "session.rolling" is disabled'
})
})
.optional()
.default((parent) => (parent.rolling ? 24 * 60 * 60 : false)), // 1 day when rolling is enabled, else false
absoluteDuration: Joi.when(Joi.ref('rolling'), {
is: false,
then: Joi.number().integer().messages({
'number.base': '"session.absoluteDuration" must be provided an integer value when "session.rolling" is false'
}),
otherwise: Joi.alternatives([Joi.number().integer(), Joi.boolean().valid(false)])
})
.optional()
.default(7 * 24 * 60 * 60), // 7 days,
name: Joi.string().token().optional().default('appSession'),
cookie: Joi.object({
domain: Joi.string().optional(),
transient: Joi.boolean().optional().default(false),
httpOnly: Joi.boolean().optional().default(true),
sameSite: Joi.string().valid('lax', 'strict', 'none').optional().default('lax'),
secure: Joi.when(Joi.ref('/baseURL'), {
is: Joi.string().pattern(isHttps),
then: Joi.boolean()
.default(true)
.custom((value, { warn }) => {
if (!value) warn('insecure.cookie');
return value;
})
.messages({
'insecure.cookie':
"Setting your cookie to insecure when over https is not recommended, I hope you know what you're doing."
}),
otherwise: Joi.boolean().valid(false).default(false).messages({
'any.only': 'Cookies set with the `Secure` property wont be attached to http requests'
})
}),
path: Joi.string().uri({ relativeOnly: true }).optional()
})
.default()
.unknown(false)
})
.default()
.unknown(false),
zIdentityLogout: Joi.boolean().optional().default(false),
authorizationParams: Joi.object({
response_type: Joi.string().optional().valid('id_token', 'code id_token', 'code').default('id_token'),
scope: Joi.string()
.optional()
.pattern(/\bopenid\b/, 'contains openid')
.default('openid profile email'),
response_mode: Joi.string()
.optional()
.when('response_type', {
is: 'code',
then: Joi.valid('query', 'form_post'),
otherwise: Joi.valid('form_post').default('form_post')
})
})
.optional()
.unknown(true)
.default(),
baseURL: Joi.string()
.uri()
.required()
.when(Joi.ref('authorizationParams.response_mode'), {
is: 'form_post',
then: Joi.string()
.pattern(isHttps)
.rule({
warn: true,
message:
"Using 'form_post' for response_mode may cause issues for you logging in over http, " +
'see https://github.com/zidentity/express-openid-connect/blob/master/FAQ.md'
})
}),
clientID: Joi.string().required(),
clientSecret: Joi.string()
.when(
Joi.ref('authorizationParams.response_type', {
adjust: (value) => value && value.includes('code')
}),
{
is: true,
then: Joi.string().required().messages({
'any.required': '"clientSecret" is required for a response_type that includes code'
})
}
)
.when(
Joi.ref('idTokenSigningAlg', {
adjust: (value) => value && value.startsWith('HS')
}),
{
is: true,
then: Joi.string().required().messages({
'any.required': '"clientSecret" is required for ID tokens with HMAC based algorithms'
})
}
),
clockTolerance: Joi.number().optional().default(60),
httpTimeout: Joi.number().optional().default(5000),
enableTelemetry: Joi.boolean().optional().default(true),
getLoginState: Joi.function()
.optional()
.default(() => getLoginState),
identityClaimFilter: Joi.array()
.optional()
.default(['aud', 'iss', 'iat', 'exp', 'nbf', 'nonce', 'azp', 'auth_time', 's_hash', 'at_hash', 'c_hash']),
idpLogout: Joi.boolean()
.optional()
.default((parent) => parent.zIdentityLogout || false),
idTokenSigningAlg: Joi.string().insensitive().not('none').optional().default('RS256'),
issuerBaseURL: Joi.string().uri().required(),
legacySameSiteCookie: Joi.boolean().optional().default(true),
routes: Joi.object({
callback: Joi.string().uri({ relativeOnly: true }).required(),
postLogoutRedirect: Joi.string().uri({ allowRelative: true }).default('')
})
.default()
.unknown(false),
clientAuthMethod: Joi.string()
.valid('client_secret_basic', 'client_secret_post', 'none')
.optional()
.default((parent) => {
return parent.authorizationParams.response_type === 'id_token' ? 'none' : 'client_secret_basic';
})
});
export type DeepPartial<T> = {
[P in keyof T]?: T[P] extends Array<infer I> ? Array<DeepPartial<I>> : DeepPartial<T[P]>;
};
export type ConfigParameters = DeepPartial<Config>;
export const get = (params: ConfigParameters = {}): Config => {
const { value, error, warning } = paramsSchema.validate(params);
if (error) {
throw new TypeError(error.details[0].message);
}
if (warning) {
console.warn(warning.message);
}
return value;
};