parent
21d20fdfd6
commit
ac2a1971a3
@ -0,0 +1,97 @@
|
|||||||
|
import { Request } from 'express';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
import { getSettings } from '../lib/settings';
|
||||||
|
|
||||||
|
export function getOIDCRedirectUrl(req: Request, state: string) {
|
||||||
|
const settings = getSettings();
|
||||||
|
const { oidcDomain, oidcClientId } = settings.main;
|
||||||
|
|
||||||
|
const url = new URL(`https://${oidcDomain}`);
|
||||||
|
url.pathname = '/authorize';
|
||||||
|
url.searchParams.set('response_type', 'code');
|
||||||
|
url.searchParams.set('client_id', oidcClientId);
|
||||||
|
|
||||||
|
const callbackUrl = new URL(
|
||||||
|
'/api/v1/auth/oidc-callback',
|
||||||
|
`http://${req.headers.host}`
|
||||||
|
).toString();
|
||||||
|
url.searchParams.set('redirect_uri', callbackUrl);
|
||||||
|
url.searchParams.set('scope', 'openid profile email');
|
||||||
|
url.searchParams.set('state', state);
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createJwtSchema = ({
|
||||||
|
oidcDomain,
|
||||||
|
oidcClientId,
|
||||||
|
}: {
|
||||||
|
oidcDomain: string;
|
||||||
|
oidcClientId: string;
|
||||||
|
}) => {
|
||||||
|
return yup.object().shape({
|
||||||
|
iss: yup
|
||||||
|
.string()
|
||||||
|
.oneOf(
|
||||||
|
[`https://${oidcDomain}`, `https://${oidcDomain}/`],
|
||||||
|
`The token iss value doesn't match the oidc_DOMAIN (${oidcDomain})`
|
||||||
|
)
|
||||||
|
.required("The token didn't come with an iss value."),
|
||||||
|
aud: yup
|
||||||
|
.string()
|
||||||
|
.oneOf(
|
||||||
|
[oidcClientId],
|
||||||
|
`The token aud value doesn't match the oidc_CLIENT_ID (${oidcClientId})`
|
||||||
|
)
|
||||||
|
.required("The token didn't come with an aud value."),
|
||||||
|
exp: yup
|
||||||
|
.number()
|
||||||
|
.required()
|
||||||
|
.test(
|
||||||
|
'is_before_date',
|
||||||
|
'Token exp value is before current time.',
|
||||||
|
(value) => {
|
||||||
|
if (!value) return false;
|
||||||
|
if (value < Math.ceil(Date.now() / 1000)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
iat: yup
|
||||||
|
.number()
|
||||||
|
.required()
|
||||||
|
.test(
|
||||||
|
'is_before_one_day',
|
||||||
|
'Token was issued before one day ago and is now invalid.',
|
||||||
|
(value) => {
|
||||||
|
if (!value) return false;
|
||||||
|
const date = new Date();
|
||||||
|
date.setDate(date.getDate() - 1);
|
||||||
|
if (value < Math.ceil(Number(date) / 1000)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
// these should exist because we set the scope to `openid profile email`
|
||||||
|
email: yup.string().email().required(),
|
||||||
|
email_verified: yup.boolean().required(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface WellKnownConfiguration {
|
||||||
|
issuer: string;
|
||||||
|
authorization_endpoint: string;
|
||||||
|
token_endpoint: string;
|
||||||
|
device_authorization_endpoint: string;
|
||||||
|
userinfo_endpoint: string;
|
||||||
|
mfa_challenge_endpoint: string;
|
||||||
|
jwks_uri: string;
|
||||||
|
registration_endpoint: string;
|
||||||
|
revocation_endpoint: string;
|
||||||
|
scopes_supported: string[];
|
||||||
|
response_types_supported: string[];
|
||||||
|
code_challenge_methods_supported: string[];
|
||||||
|
response_modes_supported: string[];
|
||||||
|
subject_types_supported: string[];
|
||||||
|
id_token_signing_alg_values_supported: string[];
|
||||||
|
token_endpoint_auth_methods_supported: string[];
|
||||||
|
claims_supported: string[];
|
||||||
|
request_uri_parameter_supported: boolean;
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
revalidate: () => void;
|
||||||
|
oidcName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function OIDCLoginButton({ revalidate, oidcName }: Props) {
|
||||||
|
const handleClick = () => {
|
||||||
|
window.location.pathname = '/api/v1/auth/oidc-login';
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="block w-full rounded-md shadow-sm">
|
||||||
|
<button
|
||||||
|
className="plex-button bg-indigo-500 hover:bg-indigo-600"
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
Login with {oidcName}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OIDCLoginButton;
|
Loading…
Reference in new issue