Skip to content

Commit e759945

Browse files
authored
chore: use refresh tokens in local oidc provider (#76)
* chore: use refresh tokens in local oidc provider * . * . * . * . * .
1 parent ae1c132 commit e759945

File tree

4 files changed

+42
-8
lines changed

4 files changed

+42
-8
lines changed

dev-auth/README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,19 @@ The provider is pre-configured with:
3333
- **Client ID**: `better-auth-dev`
3434
- **Client Secret**: `dev-secret-change-in-production`
3535
- **Test User**: `[email protected]` (Test User)
36-
- **Supported Scopes**: openid, email, profile
36+
- **Supported Scopes**: openid, email, profile, offline_access
3737
- **Redirect URIs**: Ports 3000-3003 supported
3838

39+
## Dev-only behavior
40+
41+
This provider is intended only for local development:
42+
43+
- Issues refresh tokens unconditionally (independent of requested scopes)
44+
- Auto-consents and auto-logs in a test user
45+
- Access tokens expire quickly (15s) to exercise the refresh flow
46+
47+
Do not use this configuration in production.
48+
3949
## For Production
4050

4151
Replace this with a real OIDC provider (Okta, Keycloak, Auth0, etc.) by updating the environment variables in `.env.local`:

dev-auth/oidc-provider.mjs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
/**
2+
* Dev-only OIDC provider for local testing.
3+
*
4+
* IMPORTANT: This is NOT for production use. It:
5+
* - Auto logs-in a test user and auto-consents
6+
* - Issues refresh tokens unconditionally (issueRefreshToken = true)
7+
* - Uses very short AccessToken TTL (15s) to exercise refresh flow
8+
* - Uses in-memory adapter and generated signing keys
9+
*/
110
import { config } from "dotenv";
211
import Provider from "oidc-provider";
312

@@ -65,16 +74,24 @@ const configuration = {
6574
},
6675
},
6776
features: {
68-
devInteractions: { enabled: true }, // Enable dev interactions for easy testing
77+
// Disable built-in dev interactions in favor of our custom auto-login
78+
// and auto-consent middleware used for local testing.
79+
devInteractions: { enabled: false },
6980
},
81+
// Explicitly declare supported scopes, including offline_access for refresh tokens
82+
// Scopes supported by the dev provider (app requests offline_access in dev and prod)
83+
scopes: ["openid", "email", "profile", "offline_access"],
7084
claims: {
7185
email: ["email", "email_verified"],
7286
profile: ["name"],
7387
},
7488
ttl: {
75-
AccessToken: 3600, // 1 hour
89+
// Short-lived access tokens to force refresh during dev
90+
AccessToken: 15, // seconds
7691
RefreshToken: 86400 * 30, // 30 days
7792
},
93+
// Dev-only: always issue refresh tokens to make the flow reliable locally
94+
issueRefreshToken: async () => true,
7895
};
7996

8097
const oidc = new Provider(ISSUER, configuration);
@@ -107,12 +124,17 @@ oidc.use(async (ctx, next) => {
107124
clientId: interaction.params.client_id,
108125
});
109126

110-
grant.addOIDCScope(
111-
interaction.params.scope
112-
?.split(" ")
113-
.filter((scope) => ["openid", "email", "profile"].includes(scope))
114-
.join(" ") || "openid email profile",
127+
// Grant requested scopes intersected with allowed ones
128+
const requestedScopes = interaction.params.scope
129+
?.split(" ")
130+
.filter(Boolean) || ["openid", "email", "profile"];
131+
const allowedScopes = ["openid", "email", "profile", "offline_access"];
132+
const grantedScopes = requestedScopes.filter((s) =>
133+
allowedScopes.includes(s),
115134
);
135+
// Dev-only provider already issues refresh tokens unconditionally via
136+
// `issueRefreshToken`, so we don't need to force-add offline_access here.
137+
grant.addOIDCScope(grantedScopes.join(" "));
116138

117139
await grant.save();
118140

src/app/signin/__tests__/signin.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ describe("SignInPage", () => {
5050
expect(authClient.signIn.oauth2).toHaveBeenCalledWith({
5151
providerId: "oidc",
5252
callbackURL: "/catalog",
53+
scopes: ["openid", "email", "profile", "offline_access"],
5354
});
5455
});
5556
});

src/app/signin/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export default function SignInPage() {
1212
const { error } = await authClient.signIn.oauth2({
1313
providerId: OIDC_PROVIDER_ID,
1414
callbackURL: "/catalog",
15+
scopes: ["openid", "email", "profile", "offline_access"],
1516
});
1617

1718
if (error) {

0 commit comments

Comments
 (0)