Skip to content

Commit

Permalink
Move oauthTokens out of Session and make available via `authLoade…
Browse files Browse the repository at this point in the history
…r` (#33)

* Run `npm i` and commit new package version in lockfile

* Add `onSuccess` param for side-effects when using `authLoader`

* Remove `oauthTokens` from session data and interfaces

* Remove mention of `oauthTokens` in README

* Remove mention of `oauthTokens` in comment

* Add to `options` object instead of a new parameter

* Add example to README
  • Loading branch information
mthadley authored Dec 2, 2024
1 parent db89c94 commit 084e0e5
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 20 deletions.
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ You can also control the pathname the user will be sent to after signing-in by p
export const loader = authLoader({ returnPathname: '/dashboard' });
```

If your application needs to persist `oauthTokens` or other auth-related information after the callback is successful, you can pass an `onSuccess` option:

```ts
export const loader = authLoader({
onSuccess: async ({ oauthTokens }) => {
await saveToDatabase(oauthTokens);
},
});
```

## Usage

### Access authentication data in your Remix application
Expand All @@ -80,10 +90,9 @@ import { authkitLoader } from '@workos-inc/authkit-remix';
export const loader = (args: LoaderFunctionArgs) => authkitLoader(args);

export function App() {

// Retrieves the user from the session or returns `null` if no user is signed in
// Other supported values include sessionId, accessToken, organizationId,
// role, permissions, entitlements, impersonator and oauthTokens
// Other supported values include `sessionId`, `accessToken`, `organizationId`,
// `role`, `permissions`, `entitlements`, and `impersonator`.
const { user, signInUrl, signUpUrl } = useLoaderData<typeof loader>();

return (
Expand Down Expand Up @@ -115,7 +124,6 @@ export async function action({ request }: ActionFunctionArgs) {
}

export default function HomePage() {

const { user, signInUrl, signUpUrl } = useLoaderData<typeof loader>();

if (!user) {
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 19 additions & 8 deletions src/authkit-callback-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { redirect, json, LoaderFunctionArgs } from '@remix-run/node';

export function authLoader(options: HandleAuthOptions = {}) {
return async function loader({ request }: LoaderFunctionArgs) {
const { returnPathname: returnPathnameOption = '/' } = options;
const { returnPathname: returnPathnameOption = '/', onSuccess } = options;

const url = new URL(request.url);

Expand All @@ -17,10 +17,11 @@ export function authLoader(options: HandleAuthOptions = {}) {

if (code) {
try {
const { accessToken, refreshToken, user, impersonator, oauthTokens } = await workos.userManagement.authenticateWithCode({
clientId: WORKOS_CLIENT_ID,
code,
});
const { accessToken, refreshToken, user, impersonator, oauthTokens } =
await workos.userManagement.authenticateWithCode({
clientId: WORKOS_CLIENT_ID,
code,
});

// Clean up params
url.searchParams.delete('code');
Expand All @@ -41,14 +42,14 @@ export function authLoader(options: HandleAuthOptions = {}) {
url.pathname = returnPathname;
}

// The refreshToken and oauthTokens should never be accesible publicly, hence why we encrypt it in the cookie session
// Alternatively you could persist the refresh token in a backend database
// The refreshToken should never be accesible publicly, hence why we encrypt it
// in the cookie session. Alternatively you could persist the refresh token in a
// backend database.
const encryptedSession = await encryptSession({
accessToken,
refreshToken,
user,
impersonator,
oauthTokens,
headers: {},
});

Expand All @@ -57,6 +58,16 @@ export function authLoader(options: HandleAuthOptions = {}) {
session.set('jwt', encryptedSession);
const cookie = await commitSession(session);

if (onSuccess) {
await onSuccess({
accessToken,
impersonator: impersonator ?? null,
oauthTokens: oauthTokens ?? null,
refreshToken,
user,
});
}

return redirect(url.toString(), {
headers: {
'Set-Cookie': cookie,
Expand Down
12 changes: 9 additions & 3 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ import { OauthTokens, User } from '@workos-inc/node';

export interface HandleAuthOptions {
returnPathname?: string;
onSuccess?: (data: AuthLoaderSuccessData) => void | Promise<void>;
}

export interface AuthLoaderSuccessData {
accessToken: string;
impersonator: Impersonator | null;
oauthTokens: OauthTokens | null;
refreshToken: string;
user: User;
}

export interface Impersonator {
Expand All @@ -14,7 +23,6 @@ export interface Session {
refreshToken: string;
user: User;
impersonator?: Impersonator;
oauthTokens?: OauthTokens;
headers: Record<string, string>;
}

Expand Down Expand Up @@ -45,7 +53,6 @@ export interface AuthorizedData {
permissions: string[];
entitlements: string[];
impersonator: Impersonator | null;
oauthTokens: OauthTokens | null;
sealedSession: string;
}

Expand All @@ -58,6 +65,5 @@ export interface UnauthorizedData {
permissions: null;
entitlements: null;
impersonator: null;
oauthTokens: null;
sealedSession: null;
}
3 changes: 0 additions & 3 deletions src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ async function updateSession(request: Request, debug: boolean) {
refreshToken,
user: session.user,
impersonator: session.impersonator,
oauthTokens: session.oauthTokens,
headers: {},
};

Expand Down Expand Up @@ -130,7 +129,6 @@ async function authkitLoader<Data = unknown>(
user: null,
accessToken: null,
impersonator: null,
oauthTokens: null,
organizationId: null,
permissions: null,
entitlements: null,
Expand Down Expand Up @@ -161,7 +159,6 @@ async function authkitLoader<Data = unknown>(
permissions,
entitlements,
impersonator: session.impersonator ?? null,
oauthTokens: session.oauthTokens ?? null,
sealedSession: cookieSession.get('jwt'),
};

Expand Down

0 comments on commit 084e0e5

Please sign in to comment.