Skip to content

Commit

Permalink
Handle errors by redirecting to the client
Browse files Browse the repository at this point in the history
  • Loading branch information
infomiho committed Mar 14, 2024
1 parent fe2730e commit adfa26f
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ function useOAuthCallbackHandler() {
async function handleCallback() {
try {
setIsLoading(true);
const query = new URLSearchParams(window.location.search);

// If we got redirect with an error, display it to the user
// and don't continue with the login process.
if (query.get('error')) {
setError(query.get('error'));
return;
}

const code = window.location.hash.slice(1);
const response = await exchangeOAuthCodeForToken({ code });
const sessionId = response.data.sessionId;
Expand Down
12 changes: 7 additions & 5 deletions waspc/data/Generator/templates/sdk/wasp/server/utils.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
{{={= =}=}}
import crypto from 'crypto'
import { Request, Response, NextFunction } from 'express'

import { readdir } from 'fs'
import { dirname } from 'path'
import { fileURLToPath } from 'url'

{=# isAuthEnabled =}
import { type AuthUser } from 'wasp/auth'
{=/ isAuthEnabled =}
Expand Down Expand Up @@ -40,3 +35,10 @@ async (req: RequestWithExtraFields, res: Response, next: NextFunction) => {
}

export const sleep = (ms: number): Promise<unknown> => new Promise((r) => setTimeout(r, ms))

export function redirect(res: Response, redirectUri: string) {
return res
.status(302)
.setHeader("Location", redirectUri)
.end();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
import { Router, Request as ExpressRequest } from "express";
import { GitHub, generateState } from "arctic";

import { HttpError } from 'wasp/server';
import { handleRejection } from "wasp/server/utils";
import { handleRejection, redirect } from "wasp/server/utils";
import type { ProviderConfig } from "wasp/auth/providers/types";
import { finishOAuthFlowAndGetRedirectUri } from "../oauth/user.js";
import { finishOAuthFlowAndGetRedirectUri, handleOAuthErrorAndGetRedirectUri } from "../oauth/user.js";
import { getStateCookieName, getValueFromCookie, setValueInCookie } from "../oauth/cookies.js";
import { callbackPath, loginPath } from "../oauth/redirect.js";
import { ensureEnvVarsForProvider } from "../oauth/env.js";
Expand Down Expand Up @@ -51,9 +50,7 @@ const _waspConfig: ProviderConfig = {
setValueInCookie(getStateCookieName(provider.id), state, res);

const url = await github.createAuthorizationURL(state, config);
return res.status(302)
.setHeader("Location", url.toString())
.end();
return redirect(res, url.toString());
}));

router.get(`/${callbackPath}`, handleRejection(async (req, res) => {
Expand All @@ -69,16 +66,11 @@ const _waspConfig: ProviderConfig = {
);

// Redirect to the client with the one time code
return res
.status(302)
.setHeader("Location", redirectUri)
.end();
} catch (e) {
// TODO: handle different errors
console.error(e);

// TODO: it makes sense to redirect to the client with the OAuth erorr!
throw new HttpError(500, "Something went wrong");
return redirect(res, redirectUri);
} catch (e) {
const { redirectUri } = handleOAuthErrorAndGetRedirectUri(e);
// Redirect to the client with the error
return redirect(res, redirectUri);
}
}));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
import { Router, Request as ExpressRequest } from "express";
import { Google, generateCodeVerifier, generateState } from "arctic";

import { HttpError } from 'wasp/server';
import { handleRejection } from "wasp/server/utils";
import { handleRejection, redirect } from "wasp/server/utils";
import type { ProviderConfig } from "wasp/auth/providers/types";
import { callbackPath, loginPath, getRedirectUriForCallback } from "../oauth/redirect.js";
import { finishOAuthFlowAndGetRedirectUri } from "../oauth/user.js";
import { finishOAuthFlowAndGetRedirectUri, handleOAuthErrorAndGetRedirectUri } from "../oauth/user.js";
import { getCodeVerifierCookieName, getStateCookieName, getValueFromCookie, setValueInCookie } from "../oauth/cookies.js";
import { ensureEnvVarsForProvider } from "../oauth/env.js";
import { mergeDefaultAndUserConfig } from "../oauth/config.js";
Expand Down Expand Up @@ -59,9 +58,7 @@ const _waspConfig: ProviderConfig = {
);

const url = await google.createAuthorizationURL(state, codeVerifier, config);
return res.status(302)
.setHeader("Location", url.toString())
.end();
return redirect(res, url.toString());
}));

router.get(`/${callbackPath}`, handleRejection(async (req, res) => {
Expand All @@ -76,16 +73,10 @@ const _waspConfig: ProviderConfig = {
_waspUserSignupFields,
);

return res
.status(302)
.setHeader("Location", redirectUri)
.end();
return redirect(res, redirectUri);
} catch (e) {
// TODO: handle different errors
console.error(e);

// TODO: it makes sense to redirect to the client with the OAuth erorr!
throw new HttpError(500, "Something went wrong");
const { redirectUri } = handleOAuthErrorAndGetRedirectUri(e);
return redirect(res, redirectUri);
}
}));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ const clientOAuthCallbackPath = '{= clientOAuthCallbackPath =}'
export function getRedirectUriForOneTimeCode(oneTimeCode: string) {
return `${config.frontendUrl}${clientOAuthCallbackPath}#${oneTimeCode}`;
}

export function getRedirectUriForError(error: string) {
return `${config.frontendUrl}${clientOAuthCallbackPath}?error=${error}`;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import {
} from 'wasp/auth/utils'
import { type {= authEntityUpper =} } from 'wasp/entities'
import { prisma } from 'wasp/server'
import { type ProviderConfig } from "wasp/auth/providers/types";
import { type UserSignupFields } from 'wasp/auth/providers/types'
import { getRedirectUriForOneTimeCode } from './redirect'
import { type UserSignupFields, type ProviderConfig } from 'wasp/auth/providers/types'
import { getRedirectUriForOneTimeCode, getRedirectUriForError } from './redirect'
import { tokenStore } from './oneTimeCode'

export async function finishOAuthFlowAndGetRedirectUri(
Expand All @@ -35,6 +34,13 @@ export async function finishOAuthFlowAndGetRedirectUri(
};
}

export function handleOAuthErrorAndGetRedirectUri(error: unknown): { redirectUri: string } {
const errorMessage = (error as any).message ?? "An unknown error occurred while trying to log in with the OAuth provider.";
return {
redirectUri: getRedirectUriForError(errorMessage),
};
}

// We need a user id to create the auth token, so we either find an existing user
// or create a new one if none exists for this provider.
async function getAuthIdFromProviderDetails(
Expand Down

0 comments on commit adfa26f

Please sign in to comment.