Skip to content

Commit 07ea8a1

Browse files
authored
feat(core): infer issuer slugs from environment variables (#225)
* feat(core): infer issuer slugs from environment variables * chore: verify falsy values
1 parent 3a54b2b commit 07ea8a1

7 files changed

Lines changed: 44 additions & 16 deletions

File tree

apps/nextjs/app-router/src/lib/auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const oauth = Object.keys(builtInOAuthProviders) as BuiltInOAuthProvider[
66
export const providers = [builtInOAuthProviders.github(), builtInOAuthProviders.gitlab(), builtInOAuthProviders.bitbucket()]
77

88
export const { api, core } = createAuth({
9-
oauth: ["github"],
9+
oauth: oauth,
1010
basePath: "/api/auth",
1111
baseURL: "http://localhost:3000",
1212
credentials: {

packages/core/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
1010

1111
### Added
1212

13+
- Added support for inferring OpenID Connect (OIDC) provider issuer slugs from environment variables. Issuer slugs can now be configured either through `provider.slugName` or an environment variable following the `PREFIX_SLUG_NAME` naming convention. [#225](https://github.com/aura-stack-ts/auth/pull/225)
14+
1315
- Added the `isProviderConnected()` client API to `createAuthClient()`, providing a client-side interface for the `GET /providers/:provider` endpoint. The API checks whether an OAuth or OpenID Connect (OIDC) provider is currently connected to the active session. [#223](https://github.com/aura-stack-ts/auth/pull/223)
1416

1517
- Added the experimental `isProviderConnected()` API for checking whether an OAuth or OpenID Connect (OIDC) provider is connected to the current session. This API complements `disconnectProvider()` by allowing applications to inspect the connection state without disconnecting the provider. [#223](https://github.com/aura-stack-ts/auth/pull/223)

packages/core/src/@types/oidc.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,4 +234,4 @@ export type OpenIDProvider<Profile extends object = Record<string, any>, Default
234234
*/
235235
scope?: string
236236
profile?: (profile: Profile) => DefaultUser | Promise<DefaultUser>
237-
} & GetRouteParams<`/${Issuer}`>
237+
} & Partial<GetRouteParams<`/${Issuer}`>>

packages/core/src/oauth/index.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { authentik } from "./authentik.ts"
2929
import { OAuthEnvSchema, OAuthProviderCredentialsSchema, OpenIDProviderSchema } from "@/schemas.ts"
3030
import { AuraAuthError } from "@/shared/errors.ts"
3131
import { createOpenIDPlaceholder } from "@/shared/oidc/resolve-provider.ts"
32+
import { isFalsy } from "@/shared/assert.ts"
3233

3334
export * from "./github.ts"
3435
export * from "./bitbucket.ts"
@@ -100,11 +101,18 @@ const isOpenIDProvider = (config: BuiltInOAuthProvider | RuntimeOAuthProvider |
100101
return typeof config === "object" && "issuer" in config && !("accessToken" in config)
101102
}
102103

103-
export const setDynamicParams = <const T extends string, P extends Record<string, unknown>>(template: T, params: P): string => {
104+
export const setDynamicParams = <const T extends string, P extends Record<string, unknown>>(
105+
template: T,
106+
params: P,
107+
id: string
108+
): string => {
104109
return template.replace(/(^|\/):([A-Za-z_][A-Za-z0-9_]*)/g, (_, prefix, key) => {
105-
const value = params[key]
106-
if (value == null) {
107-
throw new AuraAuthError({ code: "OIDC_INVALID_ISSUER_PARAMS" })
110+
const value = getEnv(`${id.replace("-", "_").toUpperCase()}_${key}`) ?? params[key]
111+
if (isFalsy(value)) {
112+
throw new AuraAuthError({
113+
code: "OIDC_INVALID_ISSUER_PARAMS",
114+
userMessage: `The "${id}" identity provider configuration is invalid. Please check issuer settings and try again.`,
115+
})
108116
}
109117
return `${prefix}${encodeURIComponent(String(value))}`
110118
})
@@ -116,7 +124,7 @@ export const defineOpenIDProviderConfig = (config: OpenIDProvider): RuntimeOAuth
116124
throw new AuraAuthError({ code: "INVALID_OAUTH_PROVIDER_SCHEMA_CONFIG", cause: parsed.error })
117125
}
118126
const envConfig = !config.clientId || !config.clientSecret ? defineOAuthEnvironment(config.id) : undefined
119-
config.issuer = setDynamicParams(config.issuer, config)
127+
config.issuer = setDynamicParams(config.issuer, config, config.id)
120128
return createOpenIDPlaceholder(config, {
121129
clientId: config.clientId || envConfig!.clientId,
122130
clientSecret: config.clientSecret || envConfig!.clientSecret,

packages/core/src/shared/oidc/resolve-provider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const resolveOpenIDProvider = async (provider: RuntimeOAuthProvider): Pro
2121
if (!issuer) {
2222
throw new Error("OIDC provider is missing issuer configuration: " + provider.id)
2323
}
24-
issuer = setDynamicParams(issuer, provider as unknown as Record<string, unknown>)
24+
issuer = setDynamicParams(issuer, provider as unknown as Record<string, unknown>, provider.id)
2525

2626
const metadata = await discoveryMetadata(issuer)
2727
const scope =
@@ -75,7 +75,7 @@ export const createOpenIDPlaceholder = (
7575
revokeToken: config.revokeToken,
7676
refreshWindow: config.refreshWindow,
7777
oidc: {
78-
issuer: setDynamicParams(config.issuer, config),
78+
issuer: setDynamicParams(config.issuer, config, config.id),
7979
},
8080
}
8181
}

packages/core/test/actions/providers/connected.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { describe, test, expect, vi, afterEach, beforeEach, expectTypeOf } from "vitest"
1+
import { describe, test, expect, vi, afterEach, beforeEach } from "vitest"
22
import { createCSRF } from "@/shared/crypto.ts"
33
import { GET, jose, oauthTokens, sessionPayload } from "@test/presets.ts"
44

packages/core/test/oauth.test.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import { describe, test, expect } from "vitest"
1+
import { describe, test, expect, vi } from "vitest"
22
import {
33
createBuiltInOAuthProviders,
44
builtInOAuthProviders,
5-
type GitHubProfile,
65
setDynamicParams,
76
defineOpenIDProviderConfig,
7+
type GitHubProfile,
88
} from "@/oauth/index.ts"
9-
import type { OAuthProviderCredentials, User } from "@/@types/index.ts"
109
import { AuraAuthError } from "@/shared/errors.ts"
11-
import { openIDCustomProvider } from "./presets.ts"
10+
import { openIDCustomProvider } from "@test/presets.ts"
11+
import type { OAuthProviderCredentials, User } from "@/@types/index.ts"
1212

1313
describe("createBuiltInOAuthProviders", () => {
1414
test("create oauth config for github", () => {
@@ -91,6 +91,24 @@ describe("createBuiltInOAuthProviders", () => {
9191
},
9292
})
9393
})
94+
95+
test("infer dynamic slugs in issuer via environment variables", () => {
96+
vi.stubEnv("AURA_AUTH_OIDC_PROVIDER_TEAMID", "1")
97+
vi.stubEnv("AURA_AUTH_OIDC_PROVIDER_APPID", "2")
98+
99+
const oidc = createBuiltInOAuthProviders([
100+
{ ...openIDCustomProvider, issuer: "https://app.com/issuer/:teamId/apps/:appId" } as any,
101+
])
102+
expect(oidc["oidc-provider"]).toMatchObject({
103+
id: "oidc-provider",
104+
name: "OIDC",
105+
clientId: "oidc_client_id",
106+
clientSecret: "oidc_client_secret",
107+
oidc: {
108+
issuer: "https://app.com/issuer/1/apps/2",
109+
},
110+
})
111+
})
94112
})
95113

96114
describe("setDynamicParams", () => {
@@ -136,7 +154,7 @@ describe("setDynamicParams", () => {
136154

137155
for (const { description, input, values, expected } of testCases) {
138156
test(description, () => {
139-
expect(setDynamicParams(input, values)).toBe(expected)
157+
expect(setDynamicParams(input, values, "acme")).toBe(expected)
140158
})
141159
}
142160
})
@@ -152,7 +170,7 @@ describe("setDynamicParams", () => {
152170

153171
for (const { description, input, values } of testCases) {
154172
test(description, () => {
155-
expect(() => setDynamicParams(input, values)).toThrow(AuraAuthError)
173+
expect(() => setDynamicParams(input, values, "acme")).toThrow(AuraAuthError)
156174
})
157175
}
158176
})

0 commit comments

Comments
 (0)