Skip to content

Commit f0c1514

Browse files
Paul Asjeschaance
andauthored
Fix refresh token auth bug (#11)
* Fix bug where new session wasn't saved correctly after authenticating via refresh token * Refactored API to improve DX * Fix sign out method * Addressed feedback * Refactor the API surface area * Use more strict typing in loader to support type inference * README updates * Ensure consistent return type for unauthorized data * Revert exports --------- Co-authored-by: Chance Strickland <[email protected]>
1 parent a7906a2 commit f0c1514

File tree

8 files changed

+228
-106
lines changed

8 files changed

+228
-106
lines changed

README.md

Lines changed: 64 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ To use the `signOut` method, you'll need to set your app's homepage in your Work
4040
Certain environment variables are optional and can be used to debug or configure cookie settings.
4141

4242
```sh
43-
WORKOS_COOKIE_MAX_AGE='600' # maximum age of the cookie in seconds. Defaults to 31 days
43+
WORKOS_COOKIE_MAX_AGE='600' # maximum age of the cookie in seconds. Defaults to 400 days
4444
WORKOS_API_HOSTNAME='api.workos.com' # base WorkOS API URL
4545
WORKOS_API_HTTPS=true # whether to use HTTPS in API calls
4646
WORKOS_API_PORT=5173 # port to use for API calls
@@ -50,15 +50,15 @@ WORKOS_API_PORT=5173 # port to use for API calls
5050

5151
### Callback route
5252

53-
WorkOS requires that you have a callback URL to redirect users back to after they've authenticated. In your Remix app, [create a new route](https://remix.run/docs/en/main/discussion/routes) and add the following:
53+
AuthKit requires that you have a callback URL to redirect users back to after they've authenticated. In your Remix app, [create a new route](https://remix.run/docs/en/main/discussion/routes) and add the following:
5454

5555
```ts
5656
import { authLoader } from '@workos-inc/authkit-remix';
5757

5858
export const loader = authLoader();
5959
```
6060

61-
Make sure this route matches the `WORKOS_REDIRECT_URI` variable and the configured redirect URI in your WorkOS dashboard. For instance if your redirect URI is `http://localhost:5173/callback` then you'd put the above code in `/app/routes/callback.ts`.
61+
Make sure this route matches the `WORKOS_REDIRECT_URI` variable and the configured redirect URI in your WorkOS dashboard. For instance if your redirect URI is `http://localhost:2884/callback` then you'd put the above code in `/app/routes/callback.ts`.
6262

6363
You can also control the pathname the user will be sent to after signing-in by passing a `returnPathname` option to `authLoader` like so:
6464

@@ -68,32 +68,46 @@ export const loader = authLoader({ returnPathname: '/dashboard' });
6868

6969
## Usage
7070

71-
### Get the current user
71+
### Access authentication data in your Remix application
7272

73-
For pages where you want to display a signed-in and signed-out view, use `withAuth` to retrieve the user profile from WorkOS.
73+
Use `authkitLoader` to configure AuthKit for your Remix application routes.
7474

75-
```jsx
75+
```tsx
76+
import type { LoaderFunctionArgs } from '@remix-run/node';
77+
import { authkitLoader } from '@workos-inc/authkit-remix';
78+
79+
export const loader = (args: LoaderFunctionArgs) => authkitLoader(args);
80+
81+
export function App() {
82+
return (
83+
<div>
84+
<p>Welcome back {user?.firstName && `, ${user?.firstName}`}</p>
85+
</div>
86+
);
87+
}
88+
```
89+
90+
For pages where you want to display a signed-in and signed-out view, use `authkitLoader` to retrieve the user profile from WorkOS. You can pass in additional data by providing a loader function directly to `authkitLoader`.
91+
92+
```tsx
7693
import type { ActionFunctionArgs, LoaderFunctionArgs } from '@remix-run/node';
7794
import { Link, useLoaderData, json, Form } from '@remix-run/react';
78-
import { getSignInUrl, getSignUpUrl, withAuth, signOut } from '@workos-inc/authkit-remix';
79-
80-
export async function loader({request}: LoaderFunctionArgs) {
81-
// additional properties include: sessionId, organizationId, role, impersonator, accessToken
82-
const {user} = await withAuth(request);
95+
import { getSignInUrl, getSignUpUrl, signOut, authkitLoader } from '@workos-inc/authkit-remix';
8396

97+
export const loader = (args: LoaderFunctionArgs) => authkitLoader(args, async ({ request, auth }) => {
8498
return json({
85-
signInUrl: await getSignInUrl(),
86-
signUpUrl: await getSignUpUrl(),
87-
user,
99+
signInUrl: await getSignInUrl();
100+
signUpUrl: await getSignUpUrl();
88101
});
89-
}
102+
});
90103

91104
export async function action({ request }: ActionFunctionArgs) {
92105
return await signOut(request);
93106
}
94107

95108
export default function HomePage() {
96109
// Retrieves the user from the session or returns `null` if no user is signed in
110+
// Other supported values include sessionId, accessToken, organizationId, role, permissions and impersonator
97111
const { user, signInUrl, signUpUrl } = useLoaderData<typeof loader>();
98112

99113
if (!user) {
@@ -119,8 +133,8 @@ export default function HomePage() {
119133

120134
For pages where a signed-in user is mandatory, you can use the `ensureSignedIn` option:
121135

122-
```jsx
123-
const { user } = await withAuth(request, { ensureSignedIn: true });
136+
```tsx
137+
export const loader = (args: LoaderFunctionArgs) => authkitLoader(args, { ensureSignedIn: true });
124138
```
125139

126140
Enabling `ensureSignedIn` will redirect users to AuthKit if they attempt to access the page without being authenticated.
@@ -133,45 +147,51 @@ Use the `signOut` method to sign out the current logged in user, end the session
133147

134148
Sometimes it is useful to obtain the access token directly, for instance to make API requests to another service.
135149

136-
```jsx
150+
```tsx
137151
import type { LoaderFunctionArgs, json } from '@remix-run/node';
138152
import { withAuth } from '@workos-inc/authkit-remix';
139153

140-
export async function loader({ request }: LoaderFunctionArgs) {
141-
const { accessToken } = await withAuth(request);
154+
export const loader = (args: LoaderFunctionArgs) =>
155+
authkitLoader(args, async ({ auth }) => {
156+
const { accessToken } = auth;
142157

143-
if (!accesstoken) {
144-
// Not signed in
145-
}
158+
if (!accessToken) {
159+
// Not signed in
160+
}
146161

147-
const serviceData = await fetch('/api/path', {
148-
headers: {
149-
Authorization: `Bearer ${accessToken}`,
150-
},
151-
});
162+
const serviceData = await fetch('/api/path', {
163+
headers: {
164+
Authorization: `Bearer ${accessToken}`,
165+
},
166+
});
152167

153-
return json({
154-
data: serviceData,
168+
return json({
169+
data: serviceData,
170+
});
155171
});
156-
}
157172
```
158173

159174
### Debugging
160175

161-
To enable debug logs, pass in the debug flag when using `withAuth`.
176+
To enable debug logs, pass in the debug flag when using `authkitLoader`.
162177

163-
```js
164-
import { withAuth, getSignInUrl, getSignUpUrl } from '@workos-inc/authkit-remix';
178+
```ts
179+
import { authkitLoader } from '@workos-inc/authkit-remix';
165180

166-
export async function loader({ request }: LoaderFunctionArgs) {
167-
const { user } = await withAuth(request, {
168-
debug: true,
169-
});
181+
export const loader = (args: LoaderFunctionArgs) => authkitLoader(args, { debug: true });
182+
```
170183

171-
return json({
172-
signInUrl: await getSignInUrl(),
173-
signUpUrl: await getSignUpUrl(),
174-
user,
175-
});
176-
}
184+
If providing a loader function, you can pass the options object as the third parameter
185+
186+
```ts
187+
import { authkitLoader } from '@workos-inc/authkit-remix';
188+
189+
export const loader = (args: LoaderFunctionArgs) =>
190+
authkitLoader(
191+
args,
192+
async ({ auth }) => {
193+
return json({ foo: 'bar' });
194+
},
195+
{ debug: true },
196+
);
177197
```

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@workos-inc/authkit-remix",
3-
"version": "0.2.1",
3+
"version": "0.3.0",
44
"description": "Authentication and session helpers for using WorkOS & AuthKit with Remix",
55
"sideEffects": false,
66
"type": "commonjs",

src/authkit-callback-route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export function authLoader(options: HandleAuthOptions = {}) {
3636
refreshToken,
3737
user,
3838
impersonator,
39+
headers: {},
3940
});
4041

4142
const session = await getSession(cookieName);

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { authLoader } from './authkit-callback-route.js';
2-
import { withAuth } from './session.js';
2+
import { authkitLoader } from './session.js';
33
import { getSignInUrl, getSignUpUrl, signOut } from './auth.js';
44

55
export {
@@ -9,5 +9,5 @@ export {
99
getSignUpUrl,
1010
signOut,
1111
//
12-
withAuth,
12+
authkitLoader,
1313
};

src/interfaces.ts

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,13 @@ export interface Impersonator {
88
email: string;
99
reason: string | null;
1010
}
11+
1112
export interface Session {
1213
accessToken: string;
1314
refreshToken: string;
1415
user: User;
1516
impersonator?: Impersonator;
16-
}
17-
18-
export interface UserInfo {
19-
user: User;
20-
sessionId: string;
21-
organizationId?: string;
22-
role?: string;
23-
permissions?: string[];
24-
impersonator?: Impersonator;
25-
accessToken: string;
26-
}
27-
export interface NoUserInfo {
28-
user: null;
29-
sessionId?: undefined;
30-
organizationId?: undefined;
31-
role?: undefined;
32-
impersonator?: undefined;
17+
headers: Record<string, string>;
3318
}
3419

3520
export interface AccessToken {
@@ -44,12 +29,27 @@ export interface GetAuthURLOptions {
4429
returnPathname?: string;
4530
}
4631

47-
export interface AuthkitMiddlewareAuth {
48-
enabled: boolean;
49-
unauthenticatedPaths: string[];
32+
export interface AuthKitLoaderOptions {
33+
ensureSignedIn?: boolean;
34+
debug?: boolean;
5035
}
5136

52-
export interface AuthkitMiddlewareOptions {
53-
debug?: boolean;
54-
middlewareAuth?: AuthkitMiddlewareAuth;
37+
export interface AuthorizedData {
38+
user: User;
39+
sessionId: string;
40+
accessToken: string;
41+
organizationId: string | null;
42+
role: string | null;
43+
permissions: string[];
44+
impersonator: Impersonator | null;
45+
}
46+
47+
export interface UnauthorizedData {
48+
user: null;
49+
sessionId: null;
50+
accessToken: null;
51+
organizationId: null;
52+
role: null;
53+
permissions: null;
54+
impersonator: null;
5555
}

0 commit comments

Comments
 (0)