diff --git a/src/lib/components/landing/AccessSelect.svelte b/src/lib/components/landing/AccessSelect.svelte index 6cec2b9..1d2c2a5 100644 --- a/src/lib/components/landing/AccessSelect.svelte +++ b/src/lib/components/landing/AccessSelect.svelte @@ -6,9 +6,9 @@ import Logo from '../logo/Logo.svelte'; -
+
diff --git a/src/lib/utils/url.ts b/src/lib/utils/url.ts index 92640a3..43f5532 100644 --- a/src/lib/utils/url.ts +++ b/src/lib/utils/url.ts @@ -127,3 +127,11 @@ export const getOath2RedirectUri = (challenge: string, redirectUri: string, clie export const gotoOath2RedirectUrl = (url: string, challenge: string, clientId: string): void => { goto(getOath2RedirectUri(challenge, url, clientId)); }; + +/** + * removes trailing slash from a url + * + * @returns {string} the url without a trailing slash + */ +export const removeTrailingSlash = (url: string): string => + url.endsWith('/') ? url.slice(0, -1) : url; diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index 92da7fc..2f819ef 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -21,6 +21,7 @@ export const load: PageServerLoad = async ({ locals, url, cookies }) => { const { session } = locals; const redirectUrl = url.searchParams.get('post_registered_redirect_url') ?? undefined; const postLoginUrl = url.searchParams.get('post_login_redirect_url') ?? undefined; + const challenge = url.searchParams.get('challenge_code') ?? undefined; const clientId = url.searchParams.get('client_id') ?? undefined; const cookie = cookies.get(authService.cookieName); @@ -29,11 +30,18 @@ export const load: PageServerLoad = async ({ locals, url, cookies }) => { ...data, redirectUrl, postLoginUrl, + challenge, clientId })); if (session.data.authenticated === true && cookie) { - throw redirect(302, postLoginUrl ?? '/success'); + const destinationUrl = postLoginUrl + ? challenge && clientId + ? getOath2RedirectUri(challenge, postLoginUrl, clientId) + : getOidcRedirectUrl(postLoginUrl) + : '/success'; + + throw redirect(302, destinationUrl); } return { @@ -43,91 +51,109 @@ export const load: PageServerLoad = async ({ locals, url, cookies }) => { export const actions: Actions = { sendOtp: async ({ request, locals }) => { - const data = await request.formData(); - const phone = data.get('phone') as string; + try { + const data = await request.formData(); + const phone = data.get('phone') as string; - if (!(await checkPhoneAvailability(phone))) { - return fail(400, { phone, phone_taken: true }); - } + if (!(await checkPhoneAvailability(phone))) { + return fail(400, { phone, phone_taken: true }); + } - if (!phone) { - return fail(400, { phone, missing: true }); - } + if (!phone) { + return fail(400, { phone, missing: true }); + } - if (!isPhoneValid(phone)) { - return fail(400, { phone, invalid: true }); - } + if (!isPhoneValid(phone)) { + return fail(400, { phone, invalid: true }); + } - const { last_sent } = locals.session.data; + const { last_sent } = locals.session.data; - if (last_sent && last_sent > Date.now() - 60000) { - return fail(400, { phone, invalid: true }); - } + if (last_sent && last_sent > Date.now() - 60000) { + return fail(400, { phone, invalid: true }); + } - const token = await send(phone); + const token = await send(phone); - await locals.session.set({ - otp_request_token: token, - last_sent: Date.now(), - phone, - verified: false, - authenticated: false - }); + await locals.session.set({ + otp_request_token: token, + last_sent: Date.now(), + phone, + verified: false, + authenticated: false + }); - return { sent: true }; + return { sent: true }; + } catch (error) { + return fail(400, { send_failed: true }); + } }, checkOtp: async ({ request, locals }) => { - const data = await request.formData(); - const password = data.get('password') as string; - const { session } = locals; - - if (!session.data.phone) { - return fail(400, { missing: true }); - } - - if (!password || !session.data.otp_request_token) { - return fail(400, { incorrect: true }); - } - - const verification = await verify(session.data.phone, password, session.data.otp_request_token); - - if (verification === 'correct') { - await locals.session.update((data) => ({ - ...data, - verified: true - })); + try { + const data = await request.formData(); + const password = data.get('password') as string; + const { session } = locals; - return { verified: true }; - } else if (verification === 'timeout') { - await locals.session.update((data) => ({ - ...data, - verified: false - })); + if (!session.data.phone) { + return fail(400, { missing: true }); + } - return fail(400, { timeout: true }); - } else { - await locals.session.update((data) => ({ - ...data, - verified: false - })); + if (!password || !session.data.otp_request_token) { + return fail(400, { incorrect: true }); + } - return fail(400, { incorrect: true }); + const verification = await verify( + session.data.phone, + password, + session.data.otp_request_token + ); + + if (verification === 'correct') { + await locals.session.update((data) => ({ + ...data, + verified: true + })); + + return { verified: true }; + } else if (verification === 'timeout') { + await locals.session.update((data) => ({ + ...data, + verified: false + })); + + return fail(400, { timeout: true }); + } else { + await locals.session.update((data) => ({ + ...data, + verified: false + })); + + return fail(400, { incorrect: true }); + } + } catch (error) { + console.log('failed'); + return fail(400, { check_failed: true }); } }, register: async ({ request, locals, cookies, url }) => { try { const data = await request.formData(); - const { session } = locals; const phone = data.get('phone') as string; - const { phone: verifiedPhone, redirectUrl = null, challenge = null, clientId = null } = session.data; + const { + phone: verifiedPhone, + redirectUrl = null, + challenge = null, + clientId = null, + verified + } = locals.session.data; if (!phone || isPhoneValid(phone) === false || !(await checkPhoneAvailability(phone))) { return fail(400, { invalid_phone: true }); } - if (!session.data.verified || verifiedPhone !== phone) { + if (!verified || verifiedPhone !== phone) { return fail(400, { invalid_phone: true }); } diff --git a/src/routes/[x+2e]well-known/openid-configuration/+server.ts b/src/routes/[x+2e]well-known/openid-configuration/+server.ts new file mode 100644 index 0000000..7f5ebe4 --- /dev/null +++ b/src/routes/[x+2e]well-known/openid-configuration/+server.ts @@ -0,0 +1,92 @@ +import { type RequestHandler, json } from '@sveltejs/kit'; +import { env } from '$env/dynamic/private'; +import { getUrl, removeTrailingSlash } from '$lib/utils/url'; + +export const GET: RequestHandler = async () => { + const authServer = getUrl(env.AUTH_URL); + + return json({ + backchannel_logout_session_supported: true, + introspection_endpoint_auth_methods_supported: ['client_secret_post', 'client_secret_basic'], + response_modes_supported: ['query', 'fragment', 'form_post'], + jwks_uri: `${authServer}oauth2/jwks`, + end_session_endpoint: `${authServer}oauth2/logout`, + grant_types_supported: ['authorization_code', 'refresh_token'], + authorization_endpoint: `${authServer}oauth2/authorize`, + response_types_supported: ['code'], + backchannel_logout_supported: true, + frontchannel_logout_session_supported: true, + userinfo_encryption_enc_values_supported: [ + 'A256CBC-HS512', + 'A256GCM', + 'A192CBC-HS384', + 'A192GCM', + 'A128CBC-HS256', + 'A128GCM' + ], + frontchannel_logout_supported: true, + request_uri_parameter_supported: true, + acr_values_supported: ['loa-4', 'loa-1', 'loa-2', 'loa-5', 'loa-3'], + id_token_signing_alg_values_supported: [ + 'none', + 'HS256', + 'HS384', + 'HS512', + 'RS256', + 'RS384', + 'RS512', + 'PS256', + 'PS384', + 'PS512' + ], + request_parameter_supported: true, + token_endpoint: `${authServer}oauth2/token`, + userinfo_endpoint: `${authServer}oauth2/userinfo`, + issuer: removeTrailingSlash(authServer), + claims_supported: ['sub', 'iss', 'auth_time', 'acr', 'sid'], + subject_types_supported: ['public'], + scopes_supported: ['openid', 'profile', 'email', 'address', 'phone'], + id_token_encryption_enc_values_supported: [ + 'A256CBC-HS512', + 'A256GCM', + 'A192CBC-HS384', + 'A192GCM', + 'A128CBC-HS256', + 'A128GCM' + ], + require_request_uri_registration: true, + id_token_encryption_alg_values_supported: [ + 'RSA-OAEP', + 'ECDH-ES', + 'RSA-OAEP-256', + 'ECDH-ES+A256KW', + 'ECDH-ES+A192KW', + 'ECDH-ES+A128KW', + 'RSA1_5' + ], + token_endpoint_auth_methods_supported: ['client_secret_post', 'client_secret_basic'], + userinfo_encryption_alg_values_supported: [ + 'RSA-OAEP', + 'ECDH-ES', + 'RSA-OAEP-256', + 'ECDH-ES+A256KW', + 'ECDH-ES+A192KW', + 'ECDH-ES+A128KW', + 'RSA1_5' + ], + code_challenge_methods_supported: ['plain', 'S256'], + introspection_endpoint: `${authServer}oauth2/introspect`, + userinfo_signing_alg_values_supported: [ + 'none', + 'HS256', + 'HS384', + 'HS512', + 'RS256', + 'RS384', + 'RS512', + 'PS256', + 'PS384', + 'PS512' + ] + }); +}; diff --git a/src/routes/success/+page.server.ts b/src/routes/success/+page.server.ts index 528589e..11fbafe 100644 --- a/src/routes/success/+page.server.ts +++ b/src/routes/success/+page.server.ts @@ -18,6 +18,6 @@ export const load: PageServerLoad = async ({ parent, cookies }) => { phone: session.phone, redirectUrl: session.redirectUrl ?? null, challenge: session.challenge ?? null, - clientId: session.clientId ?? null, + clientId: session.clientId ?? null }; };