workers-gate 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -98,6 +98,8 @@ The contract: `authorizeUrl` must request whatever scope/claims `identify` needs
98
98
 
99
99
  `silent` is `true` when a returning visitor's session just expired; OAuth-style providers can translate it to `prompt=none`. If the IdP answers the callback with an `error` param, the gate automatically retries an interactive login. `oauthProvider`'s `identify` also receives the full `tokenResponse` for extras like `id_token`.
100
100
 
101
+ `oauthProvider` takes two param bags: `authorizeParams` goes on every authorize URL; `silentParams` only on silent re-authorization and wins on conflicts. Which one `prompt=none` belongs in depends on how the IdP reads it. If it means "skip the consent screen when already authorized" and first-timers still get the screen (Discord, for one), put it in `authorizeParams`. If it means "never interact" and not-yet-authorized users get an `error` back (OIDC proper), put it in `silentParams` so first logins stay interactive.
102
+
101
103
  ## Recipes
102
104
 
103
105
  The library ships no IdP presets — endpoints, scope, and policy are yours. Both example projects are deployable as-is (wrangler config, `.dev.vars` template, generated types).
@@ -111,7 +113,8 @@ provider: oauthProvider({
111
113
  clientId: env.DISCORD_CLIENT_ID,
112
114
  clientSecret: env.DISCORD_CLIENT_SECRET,
113
115
  scope: "guilds.members.read",
114
- silentParams: { prompt: "none" },
116
+ // Discord's prompt=none only skips consent for already-authorized users
117
+ authorizeParams: { prompt: "none" },
115
118
  async identify({ accessToken }) {
116
119
  // membership in this one guild, straight from Discord; 404 = not a member
117
120
  const res = await fetch(
package/dist/oauth.d.ts CHANGED
@@ -14,7 +14,9 @@ export interface OAuthProviderConfig<Data extends {}> {
14
14
  clientSecret: string;
15
15
  /** Must cover whatever identify() fetches — scope and identify are a pair */
16
16
  scope?: string;
17
- /** Extra query params appended on silent re-authorization, e.g. { prompt: "none" } */
17
+ /** Extra query params appended to every authorize URL, e.g. { access_type: "offline" } */
18
+ authorizeParams?: Record<string, string>;
19
+ /** Extra query params appended only on silent re-authorization; they win over authorizeParams */
18
20
  silentParams?: Record<string, string>;
19
21
  /** Fetch the data about the visitor that the gate's filter will judge */
20
22
  identify(input: OAuthIdentifyInput): Promise<Data | null>;
package/dist/oauth.js CHANGED
@@ -16,11 +16,14 @@ export function oauthProvider(config) {
16
16
  if (config.scope)
17
17
  url.searchParams.set("scope", config.scope);
18
18
  url.searchParams.set("state", state);
19
- if (silent) {
20
- for (const [key, value] of Object.entries(config.silentParams ?? {})) {
19
+ const apply = (params = {}) => {
20
+ for (const [key, value] of Object.entries(params)) {
21
21
  url.searchParams.set(key, value);
22
22
  }
23
- }
23
+ };
24
+ apply(config.authorizeParams);
25
+ if (silent)
26
+ apply(config.silentParams);
24
27
  return url.toString();
25
28
  },
26
29
  async identify({ code, redirectUri }) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workers-gate",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "Stateless authorization gate for apps on Cloudflare Workers — bring your own provider, no database, cookie signing delegated to jose",
5
5
  "keywords": [
6
6
  "authorization",
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/node": "^26.1.0",
53
- "oxfmt": "^0.67.0",
53
+ "oxfmt": "^0.75.0",
54
54
  "oxlint": "^1.72.0",
55
55
  "typescript": "^7.0.2",
56
56
  "vitest": "^5.0.0"