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
};
};