Skip to content

Commit

Permalink
Add returnTo option to signOut (#174)
Browse files Browse the repository at this point in the history
* Depend on `@workos-inc/[email protected]`

* Add `returnTo` option to `signOut`

* Update `signOut` docs in README to reference Logout URIs
  • Loading branch information
mthadley authored Jan 15, 2025
1 parent b5db0de commit 114dc27
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 12 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ NEXT_PUBLIC_WORKOS_REDIRECT_URI="http://localhost:3000/callback" # configured in
openssl rand -base64 24
```

To use the `signOut` method, you'll need to set your app's homepage in your WorkOS dashboard settings under "Redirects".
To use the `signOut` method, you'll need to set a default Logout URI in your WorkOS dashboard settings under "Redirects".

### Optional configuration

Expand Down Expand Up @@ -336,7 +336,13 @@ export const config = { matcher: ['/', '/account/:path*'] };
### Signing out
Use the `signOut` method to sign out the current logged in user and redirect to your app's homepage. The homepage redirect is set in your WorkOS dashboard settings under "Redirect".
Use the `signOut` method to sign out the current logged in user and redirect to your app's default Logout URI. The Logout URI is set in your WorkOS dashboard settings under "Redirect".
To use a non-default Logout URI, you can use the `returnTo` parameter.
```tsx
await signOut({ returnTo: 'https://your-app.com/signed-out' });
```
### Visualizing an impersonation
Expand Down
35 changes: 35 additions & 0 deletions __tests__/auth.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { describe, it, expect, beforeEach, jest } from '@jest/globals';

import { getSignInUrl, getSignUpUrl, signOut } from '../src/auth.js';
import * as session from '../src/session';

// These are mocked in jest.setup.ts
import { cookies, headers } from 'next/headers';
import { redirect } from 'next/navigation';
import { UserInfo } from '../src/interfaces';

jest.mock('../src/session', () => {
const actual = jest.requireActual<typeof session>('../src/session');

return { ...actual, terminateSession: jest.fn(actual.terminateSession) };
});

describe('auth.ts', () => {
beforeEach(async () => {
Expand Down Expand Up @@ -73,5 +81,32 @@ describe('auth.ts', () => {
const sessionCookie = nextCookies.get('wos-session');
expect(sessionCookie).toBeUndefined();
});

describe('when given a `returnTo` parameter', () => {
it('passes the `returnTo` through to `terminateSession`', async () => {
const nextHeaders = await headers();

nextHeaders.set('x-workos-middleware', 'true');

await signOut({ returnTo: 'https://example.com/signed-out' });

expect(redirect).toHaveBeenCalledTimes(1);
expect(redirect).toHaveBeenCalledWith('https://example.com/signed-out');
expect(session.terminateSession).toHaveBeenCalledWith({ returnTo: 'https://example.com/signed-out' });
});

describe('when there is no session', () => {
it('returns to the `returnTo`', async () => {
const nextHeaders = await headers();

nextHeaders.set('x-workos-middleware', 'true');

await signOut({ returnTo: 'https://example.com/signed-out' });

expect(redirect).toHaveBeenCalledTimes(1);
expect(redirect).toHaveBeenCalledWith('https://example.com/signed-out');
});
});
});
});
});
21 changes: 21 additions & 0 deletions __tests__/session.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -778,5 +778,26 @@ describe('session.ts', () => {
expect(redirect).toHaveBeenCalledTimes(1);
expect(redirect).toHaveBeenCalledWith('/');
});

describe('when given a `returnTo` URL', () => {
it('includes a `return_to` query parameter in the logout URL', async () => {
const nextHeaders = await headers();
nextHeaders.set('x-url', 'http://example.com/protected');

mockSession.accessToken = await generateTestToken();

nextHeaders.set(
'x-workos-session',
await sealData(mockSession, { password: process.env.WORKOS_COOKIE_PASSWORD as string }),
);

await terminateSession({ returnTo: 'http://example.com/signed-out' });

expect(redirect).toHaveBeenCalledTimes(1);
expect(redirect).toHaveBeenCalledWith(
'https://api.workos.com/user_management/sessions/logout?session_id=session_123&return_to=http%3A%2F%2Fexample.com%2Fsigned-out',
);
});
});
});
});
8 changes: 4 additions & 4 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"format": "prettier \"{src,__tests__}/**/*.{js,ts,tsx}\" --write"
},
"dependencies": {
"@workos-inc/node": "^7.33.0",
"@workos-inc/node": "^7.37.0",
"iron-session": "^8.0.1",
"jose": "^5.2.3",
"path-to-regexp": "^6.2.2"
Expand Down
4 changes: 2 additions & 2 deletions src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ async function getSignUpUrl() {
return getAuthorizationUrl({ screenHint: 'sign-up' });
}

async function signOut() {
async function signOut({ returnTo }: { returnTo?: string } = {}) {
const cookie: { name: string; domain?: string } = {
name: WORKOS_COOKIE_NAME || 'wos-session',
};
Expand All @@ -22,7 +22,7 @@ async function signOut() {
const nextCookies = await cookies();

nextCookies.delete(cookie);
await terminateSession();
await terminateSession({ returnTo });
}

export { getSignInUrl, getSignUpUrl, signOut };
6 changes: 3 additions & 3 deletions src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,12 +382,12 @@ async function withAuth({ ensureSignedIn = false }: { ensureSignedIn?: boolean }
};
}

async function terminateSession() {
async function terminateSession({ returnTo }: { returnTo?: string } = {}) {
const { sessionId } = await withAuth();
if (sessionId) {
redirect(workos.userManagement.getLogoutUrl({ sessionId }));
redirect(workos.userManagement.getLogoutUrl({ sessionId, returnTo }));
} else {
redirect('/');
redirect(returnTo ?? '/');
}
}

Expand Down

0 comments on commit 114dc27

Please sign in to comment.