@backstage/plugin-auth-backend-module-cloudflare-access-provider 0.4.6-next.0 → 0.4.7-next.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @backstage/plugin-auth-backend-module-cloudflare-access-provider
2
2
 
3
+ ## 0.4.7-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @backstage/plugin-auth-node@0.6.7-next.0
9
+ - @backstage/backend-plugin-api@1.4.3-next.0
10
+
11
+ ## 0.4.6
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies
16
+ - @backstage/plugin-auth-node@0.6.6
17
+ - @backstage/backend-plugin-api@1.4.2
18
+
3
19
  ## 0.4.6-next.0
4
20
 
5
21
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"authenticator.cjs.js","sources":["../src/authenticator.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CacheService } from '@backstage/backend-plugin-api';\nimport {\n ProxyAuthenticator,\n createProxyAuthenticator,\n} from '@backstage/plugin-auth-node';\nimport { AuthHelper } from './helpers';\nimport { CloudflareAccessResult } from './types';\n\n/**\n * Implements Cloudflare Access authentication.\n *\n * @public\n */\nexport function createCloudflareAccessAuthenticator(options?: {\n cache?: CacheService;\n}): ProxyAuthenticator<\n unknown,\n CloudflareAccessResult,\n CloudflareAccessResult\n> {\n return createProxyAuthenticator({\n async defaultProfileTransform(result: CloudflareAccessResult) {\n return {\n profile: {\n email: result.cfIdentity.email,\n displayName: result.cfIdentity.name,\n },\n };\n },\n initialize({ config }) {\n return {\n helper: AuthHelper.fromConfig(config, { cache: options?.cache }),\n };\n },\n async authenticate({ req }, { helper }) {\n const result = await helper.authenticate(req);\n return {\n result,\n providerInfo: result,\n };\n },\n });\n}\n"],"names":["createProxyAuthenticator","AuthHelper"],"mappings":";;;;;AA6BO,SAAS,oCAAoC,OAMlD,EAAA;AACA,EAAA,OAAOA,uCAAyB,CAAA;AAAA,IAC9B,MAAM,wBAAwB,MAAgC,EAAA;AAC5D,MAAO,OAAA;AAAA,QACL,OAAS,EAAA;AAAA,UACP,KAAA,EAAO,OAAO,UAAW,CAAA,KAAA;AAAA,UACzB,WAAA,EAAa,OAAO,UAAW,CAAA;AAAA;AACjC,OACF;AAAA,KACF;AAAA,IACA,UAAA,CAAW,EAAE,MAAA,EAAU,EAAA;AACrB,MAAO,OAAA;AAAA,QACL,MAAA,EAAQC,mBAAW,UAAW,CAAA,MAAA,EAAQ,EAAE,KAAO,EAAA,OAAA,EAAS,OAAO;AAAA,OACjE;AAAA,KACF;AAAA,IACA,MAAM,YAAa,CAAA,EAAE,KAAO,EAAA,EAAE,QAAU,EAAA;AACtC,MAAA,MAAM,MAAS,GAAA,MAAM,MAAO,CAAA,YAAA,CAAa,GAAG,CAAA;AAC5C,MAAO,OAAA;AAAA,QACL,MAAA;AAAA,QACA,YAAc,EAAA;AAAA,OAChB;AAAA;AACF,GACD,CAAA;AACH;;;;"}
1
+ {"version":3,"file":"authenticator.cjs.js","sources":["../src/authenticator.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CacheService } from '@backstage/backend-plugin-api';\nimport {\n ProxyAuthenticator,\n createProxyAuthenticator,\n} from '@backstage/plugin-auth-node';\nimport { AuthHelper } from './helpers';\nimport { CloudflareAccessResult } from './types';\n\n/**\n * Implements Cloudflare Access authentication.\n *\n * @public\n */\nexport function createCloudflareAccessAuthenticator(options?: {\n cache?: CacheService;\n}): ProxyAuthenticator<\n unknown,\n CloudflareAccessResult,\n CloudflareAccessResult\n> {\n return createProxyAuthenticator({\n async defaultProfileTransform(result: CloudflareAccessResult) {\n return {\n profile: {\n email: result.cfIdentity.email,\n displayName: result.cfIdentity.name,\n },\n };\n },\n initialize({ config }) {\n return {\n helper: AuthHelper.fromConfig(config, { cache: options?.cache }),\n };\n },\n async authenticate({ req }, { helper }) {\n const result = await helper.authenticate(req);\n return {\n result,\n providerInfo: result,\n };\n },\n });\n}\n"],"names":["createProxyAuthenticator","AuthHelper"],"mappings":";;;;;AA6BO,SAAS,oCAAoC,OAAA,EAMlD;AACA,EAAA,OAAOA,uCAAA,CAAyB;AAAA,IAC9B,MAAM,wBAAwB,MAAA,EAAgC;AAC5D,MAAA,OAAO;AAAA,QACL,OAAA,EAAS;AAAA,UACP,KAAA,EAAO,OAAO,UAAA,CAAW,KAAA;AAAA,UACzB,WAAA,EAAa,OAAO,UAAA,CAAW;AAAA;AACjC,OACF;AAAA,IACF,CAAA;AAAA,IACA,UAAA,CAAW,EAAE,MAAA,EAAO,EAAG;AACrB,MAAA,OAAO;AAAA,QACL,MAAA,EAAQC,mBAAW,UAAA,CAAW,MAAA,EAAQ,EAAE,KAAA,EAAO,OAAA,EAAS,OAAO;AAAA,OACjE;AAAA,IACF,CAAA;AAAA,IACA,MAAM,YAAA,CAAa,EAAE,KAAI,EAAG,EAAE,QAAO,EAAG;AACtC,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,YAAA,CAAa,GAAG,CAAA;AAC5C,MAAA,OAAO;AAAA,QACL,MAAA;AAAA,QACA,YAAA,EAAc;AAAA,OAChB;AAAA,IACF;AAAA,GACD,CAAA;AACH;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.cjs.js","sources":["../src/helpers.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CacheService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport {\n AuthenticationError,\n ForwardedError,\n ResponseError,\n} from '@backstage/errors';\nimport express from 'express';\nimport { createRemoteJWKSet, jwtVerify } from 'jose';\nimport {\n CACHE_PREFIX,\n CF_JWT_HEADER,\n COOKIE_AUTH_NAME,\n CloudflareAccessClaims,\n CloudflareAccessIdentityProfile,\n CloudflareAccessResult,\n ServiceToken,\n} from './types';\n\nexport class AuthHelper {\n static fromConfig(\n config: Config,\n options?: { cache?: CacheService },\n ): AuthHelper {\n const teamName = config.getString('teamName');\n const jwtHeaderName =\n config.getOptionalString('jwtHeaderName') ?? CF_JWT_HEADER;\n const authorizationCookieName =\n config.getOptionalString('authorizationCookieName') ?? COOKIE_AUTH_NAME;\n const serviceTokens = (\n config.getOptionalConfigArray('serviceTokens') ?? []\n )?.map(cfg => {\n return {\n token: cfg.getString('token'),\n subject: cfg.getString('subject'),\n } as ServiceToken;\n });\n\n const keySet = createRemoteJWKSet(\n new URL(`https://${teamName}.cloudflareaccess.com/cdn-cgi/access/certs`),\n );\n\n return new AuthHelper(\n teamName,\n serviceTokens,\n jwtHeaderName,\n authorizationCookieName,\n keySet,\n options?.cache,\n );\n }\n\n private constructor(\n private readonly teamName: string,\n private readonly serviceTokens: ServiceToken[],\n private readonly jwtHeaderName: string,\n private readonly authorizationCookieName: string,\n private readonly keySet: ReturnType<typeof createRemoteJWKSet>,\n private readonly cache?: CacheService,\n ) {}\n\n async authenticate(req: express.Request): Promise<CloudflareAccessResult> {\n // JWTs generated by Access are available in a request header as\n // Cf-Access-Jwt-Assertion and as cookies as CF_Authorization.\n let jwt = req.header(this.jwtHeaderName);\n if (!jwt) {\n jwt = req.cookies[this.authorizationCookieName];\n }\n if (!jwt) {\n // Only throw if both are not provided by Cloudflare Access since either\n // can be used.\n throw new AuthenticationError(\n `Missing ${this.jwtHeaderName} and ${this.authorizationCookieName} from Cloudflare Access`,\n );\n }\n\n // Cloudflare signs the JWT using the RSA Signature with SHA-256 (RS256).\n // RS256 follows an asymmetric algorithm; a private key signs the JWTs and\n // a separate public key verifies the signature.\n const verifyResult = await jwtVerify(jwt, this.keySet, {\n issuer: `https://${this.teamName}.cloudflareaccess.com`,\n });\n\n const isServiceToken = !verifyResult.payload.sub;\n\n const subject = isServiceToken\n ? (verifyResult.payload.common_name as string)\n : verifyResult.payload.sub;\n if (!subject) {\n throw new AuthenticationError(\n `Missing both sub and common_name from Cloudflare Access JWT`,\n );\n }\n\n const serviceToken = this.serviceTokens.find(st => st.token === subject);\n if (isServiceToken && !serviceToken) {\n throw new AuthenticationError(\n `${subject} is not a permitted Service Token.`,\n );\n }\n\n const cacheKey = `${CACHE_PREFIX}/${subject}`;\n const cfAccessResultStr = await this.cache?.get(cacheKey);\n if (typeof cfAccessResultStr === 'string') {\n const result = JSON.parse(cfAccessResultStr) as CloudflareAccessResult;\n return {\n ...result,\n token: jwt,\n };\n }\n const claims = verifyResult.payload as CloudflareAccessClaims;\n\n // Builds a passport profile from JWT claims first\n try {\n let cfIdentity: CloudflareAccessIdentityProfile;\n if (serviceToken) {\n cfIdentity = {\n id: subject,\n name: 'Bot',\n email: serviceToken.subject,\n groups: [],\n };\n } else {\n // If we successfully fetch the get-identity endpoint,\n // We supplement the passport profile with richer user identity\n // information here.\n cfIdentity = await this.getIdentityProfile(jwt);\n }\n // Stores a stringified JSON object in cfaccess provider cache only when\n // we complete all steps\n const cfAccessResult = {\n claims,\n cfIdentity,\n expiresInSeconds: claims.exp - claims.iat,\n };\n this.cache?.set(cacheKey, JSON.stringify(cfAccessResult));\n return {\n ...cfAccessResult,\n token: jwt,\n };\n } catch (err) {\n throw new ForwardedError(\n 'Failed to populate access identity information',\n err,\n );\n }\n }\n\n private async getIdentityProfile(\n jwt: string,\n ): Promise<CloudflareAccessIdentityProfile> {\n const headers = new Headers();\n // set both headers just the way inbound responses are set\n headers.set(this.jwtHeaderName, jwt);\n headers.set('cookie', `${this.authorizationCookieName}=${jwt}`);\n try {\n const res = await fetch(\n `https://${this.teamName}.cloudflareaccess.com/cdn-cgi/access/get-identity`,\n { headers },\n );\n if (!res.ok) {\n throw await ResponseError.fromResponse(res);\n }\n const cfIdentity = await res.json();\n return cfIdentity as unknown as CloudflareAccessIdentityProfile;\n } catch (err) {\n throw new ForwardedError('getIdentityProfile failed', err);\n }\n }\n}\n"],"names":["CF_JWT_HEADER","COOKIE_AUTH_NAME","createRemoteJWKSet","AuthenticationError","jwtVerify","CACHE_PREFIX","ForwardedError","ResponseError"],"mappings":"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"}
1
+ {"version":3,"file":"helpers.cjs.js","sources":["../src/helpers.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CacheService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport {\n AuthenticationError,\n ForwardedError,\n ResponseError,\n} from '@backstage/errors';\nimport express from 'express';\nimport { createRemoteJWKSet, jwtVerify } from 'jose';\nimport {\n CACHE_PREFIX,\n CF_JWT_HEADER,\n COOKIE_AUTH_NAME,\n CloudflareAccessClaims,\n CloudflareAccessIdentityProfile,\n CloudflareAccessResult,\n ServiceToken,\n} from './types';\n\nexport class AuthHelper {\n static fromConfig(\n config: Config,\n options?: { cache?: CacheService },\n ): AuthHelper {\n const teamName = config.getString('teamName');\n const jwtHeaderName =\n config.getOptionalString('jwtHeaderName') ?? CF_JWT_HEADER;\n const authorizationCookieName =\n config.getOptionalString('authorizationCookieName') ?? COOKIE_AUTH_NAME;\n const serviceTokens = (\n config.getOptionalConfigArray('serviceTokens') ?? []\n )?.map(cfg => {\n return {\n token: cfg.getString('token'),\n subject: cfg.getString('subject'),\n } as ServiceToken;\n });\n\n const keySet = createRemoteJWKSet(\n new URL(`https://${teamName}.cloudflareaccess.com/cdn-cgi/access/certs`),\n );\n\n return new AuthHelper(\n teamName,\n serviceTokens,\n jwtHeaderName,\n authorizationCookieName,\n keySet,\n options?.cache,\n );\n }\n\n private constructor(\n private readonly teamName: string,\n private readonly serviceTokens: ServiceToken[],\n private readonly jwtHeaderName: string,\n private readonly authorizationCookieName: string,\n private readonly keySet: ReturnType<typeof createRemoteJWKSet>,\n private readonly cache?: CacheService,\n ) {}\n\n async authenticate(req: express.Request): Promise<CloudflareAccessResult> {\n // JWTs generated by Access are available in a request header as\n // Cf-Access-Jwt-Assertion and as cookies as CF_Authorization.\n let jwt = req.header(this.jwtHeaderName);\n if (!jwt) {\n jwt = req.cookies[this.authorizationCookieName];\n }\n if (!jwt) {\n // Only throw if both are not provided by Cloudflare Access since either\n // can be used.\n throw new AuthenticationError(\n `Missing ${this.jwtHeaderName} and ${this.authorizationCookieName} from Cloudflare Access`,\n );\n }\n\n // Cloudflare signs the JWT using the RSA Signature with SHA-256 (RS256).\n // RS256 follows an asymmetric algorithm; a private key signs the JWTs and\n // a separate public key verifies the signature.\n const verifyResult = await jwtVerify(jwt, this.keySet, {\n issuer: `https://${this.teamName}.cloudflareaccess.com`,\n });\n\n const isServiceToken = !verifyResult.payload.sub;\n\n const subject = isServiceToken\n ? (verifyResult.payload.common_name as string)\n : verifyResult.payload.sub;\n if (!subject) {\n throw new AuthenticationError(\n `Missing both sub and common_name from Cloudflare Access JWT`,\n );\n }\n\n const serviceToken = this.serviceTokens.find(st => st.token === subject);\n if (isServiceToken && !serviceToken) {\n throw new AuthenticationError(\n `${subject} is not a permitted Service Token.`,\n );\n }\n\n const cacheKey = `${CACHE_PREFIX}/${subject}`;\n const cfAccessResultStr = await this.cache?.get(cacheKey);\n if (typeof cfAccessResultStr === 'string') {\n const result = JSON.parse(cfAccessResultStr) as CloudflareAccessResult;\n return {\n ...result,\n token: jwt,\n };\n }\n const claims = verifyResult.payload as CloudflareAccessClaims;\n\n // Builds a passport profile from JWT claims first\n try {\n let cfIdentity: CloudflareAccessIdentityProfile;\n if (serviceToken) {\n cfIdentity = {\n id: subject,\n name: 'Bot',\n email: serviceToken.subject,\n groups: [],\n };\n } else {\n // If we successfully fetch the get-identity endpoint,\n // We supplement the passport profile with richer user identity\n // information here.\n cfIdentity = await this.getIdentityProfile(jwt);\n }\n // Stores a stringified JSON object in cfaccess provider cache only when\n // we complete all steps\n const cfAccessResult = {\n claims,\n cfIdentity,\n expiresInSeconds: claims.exp - claims.iat,\n };\n this.cache?.set(cacheKey, JSON.stringify(cfAccessResult));\n return {\n ...cfAccessResult,\n token: jwt,\n };\n } catch (err) {\n throw new ForwardedError(\n 'Failed to populate access identity information',\n err,\n );\n }\n }\n\n private async getIdentityProfile(\n jwt: string,\n ): Promise<CloudflareAccessIdentityProfile> {\n const headers = new Headers();\n // set both headers just the way inbound responses are set\n headers.set(this.jwtHeaderName, jwt);\n headers.set('cookie', `${this.authorizationCookieName}=${jwt}`);\n try {\n const res = await fetch(\n `https://${this.teamName}.cloudflareaccess.com/cdn-cgi/access/get-identity`,\n { headers },\n );\n if (!res.ok) {\n throw await ResponseError.fromResponse(res);\n }\n const cfIdentity = await res.json();\n return cfIdentity as unknown as CloudflareAccessIdentityProfile;\n } catch (err) {\n throw new ForwardedError('getIdentityProfile failed', err);\n }\n }\n}\n"],"names":["CF_JWT_HEADER","COOKIE_AUTH_NAME","createRemoteJWKSet","AuthenticationError","jwtVerify","CACHE_PREFIX","ForwardedError","ResponseError"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"module.cjs.js","sources":["../src/module.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n coreServices,\n createBackendModule,\n} from '@backstage/backend-plugin-api';\nimport {\n authProvidersExtensionPoint,\n commonSignInResolvers,\n createProxyAuthProviderFactory,\n} from '@backstage/plugin-auth-node';\nimport { createCloudflareAccessAuthenticator } from './authenticator';\nimport { cloudflareAccessSignInResolvers } from './resolvers';\n\n/**\n * The Cloudflare Access provider backend module for the auth plugin.\n *\n * @public\n */\nexport const authModuleCloudflareAccessProvider = createBackendModule({\n pluginId: 'auth',\n moduleId: 'cloudflare-access-provider',\n register(reg) {\n reg.registerInit({\n deps: {\n authProviders: authProvidersExtensionPoint,\n cache: coreServices.cache,\n },\n async init({ authProviders, cache }) {\n authProviders.registerProvider({\n providerId: 'cfaccess',\n factory: createProxyAuthProviderFactory({\n authenticator: createCloudflareAccessAuthenticator({ cache }),\n signInResolverFactories: {\n ...cloudflareAccessSignInResolvers,\n ...commonSignInResolvers,\n },\n }),\n });\n },\n });\n },\n});\n"],"names":["createBackendModule","authProvidersExtensionPoint","coreServices","createProxyAuthProviderFactory","createCloudflareAccessAuthenticator","cloudflareAccessSignInResolvers","commonSignInResolvers"],"mappings":";;;;;;;AAiCO,MAAM,qCAAqCA,oCAAoB,CAAA;AAAA,EACpE,QAAU,EAAA,MAAA;AAAA,EACV,QAAU,EAAA,4BAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,aAAe,EAAAC,0CAAA;AAAA,QACf,OAAOC,6BAAa,CAAA;AAAA,OACtB;AAAA,MACA,MAAM,IAAA,CAAK,EAAE,aAAA,EAAe,OAAS,EAAA;AACnC,QAAA,aAAA,CAAc,gBAAiB,CAAA;AAAA,UAC7B,UAAY,EAAA,UAAA;AAAA,UACZ,SAASC,6CAA+B,CAAA;AAAA,YACtC,aAAe,EAAAC,iDAAA,CAAoC,EAAE,KAAA,EAAO,CAAA;AAAA,YAC5D,uBAAyB,EAAA;AAAA,cACvB,GAAGC,yCAAA;AAAA,cACH,GAAGC;AAAA;AACL,WACD;AAAA,SACF,CAAA;AAAA;AACH,KACD,CAAA;AAAA;AAEL,CAAC;;;;"}
1
+ {"version":3,"file":"module.cjs.js","sources":["../src/module.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n coreServices,\n createBackendModule,\n} from '@backstage/backend-plugin-api';\nimport {\n authProvidersExtensionPoint,\n commonSignInResolvers,\n createProxyAuthProviderFactory,\n} from '@backstage/plugin-auth-node';\nimport { createCloudflareAccessAuthenticator } from './authenticator';\nimport { cloudflareAccessSignInResolvers } from './resolvers';\n\n/**\n * The Cloudflare Access provider backend module for the auth plugin.\n *\n * @public\n */\nexport const authModuleCloudflareAccessProvider = createBackendModule({\n pluginId: 'auth',\n moduleId: 'cloudflare-access-provider',\n register(reg) {\n reg.registerInit({\n deps: {\n authProviders: authProvidersExtensionPoint,\n cache: coreServices.cache,\n },\n async init({ authProviders, cache }) {\n authProviders.registerProvider({\n providerId: 'cfaccess',\n factory: createProxyAuthProviderFactory({\n authenticator: createCloudflareAccessAuthenticator({ cache }),\n signInResolverFactories: {\n ...cloudflareAccessSignInResolvers,\n ...commonSignInResolvers,\n },\n }),\n });\n },\n });\n },\n});\n"],"names":["createBackendModule","authProvidersExtensionPoint","coreServices","createProxyAuthProviderFactory","createCloudflareAccessAuthenticator","cloudflareAccessSignInResolvers","commonSignInResolvers"],"mappings":";;;;;;;AAiCO,MAAM,qCAAqCA,oCAAA,CAAoB;AAAA,EACpE,QAAA,EAAU,MAAA;AAAA,EACV,QAAA,EAAU,4BAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,aAAA,EAAeC,0CAAA;AAAA,QACf,OAAOC,6BAAA,CAAa;AAAA,OACtB;AAAA,MACA,MAAM,IAAA,CAAK,EAAE,aAAA,EAAe,OAAM,EAAG;AACnC,QAAA,aAAA,CAAc,gBAAA,CAAiB;AAAA,UAC7B,UAAA,EAAY,UAAA;AAAA,UACZ,SAASC,6CAAA,CAA+B;AAAA,YACtC,aAAA,EAAeC,iDAAA,CAAoC,EAAE,KAAA,EAAO,CAAA;AAAA,YAC5D,uBAAA,EAAyB;AAAA,cACvB,GAAGC,yCAAA;AAAA,cACH,GAAGC;AAAA;AACL,WACD;AAAA,SACF,CAAA;AAAA,MACH;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"resolvers.cjs.js","sources":["../src/resolvers.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n createSignInResolverFactory,\n SignInInfo,\n} from '@backstage/plugin-auth-node';\nimport { CloudflareAccessResult } from './types';\nimport { z } from 'zod';\n\n/**\n * Available sign-in resolvers for the Cloudflare Access auth provider.\n *\n * @public\n */\nexport namespace cloudflareAccessSignInResolvers {\n /**\n * Looks up the user by matching their email to the entity email.\n */\n export const emailMatchingUserEntityProfileEmail =\n createSignInResolverFactory({\n optionsSchema: z\n .object({\n dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(),\n })\n .optional(),\n create(options = {}) {\n return async (info: SignInInfo<CloudflareAccessResult>, ctx) => {\n const { profile } = info;\n\n if (!profile.email) {\n throw new Error(\n 'Login failed, user profile does not contain an email',\n );\n }\n\n return ctx.signInWithCatalogUser(\n {\n filter: {\n 'spec.profile.email': profile.email,\n },\n },\n {\n dangerousEntityRefFallback:\n options?.dangerouslyAllowSignInWithoutUserInCatalog\n ? { entityRef: { name: profile.email } }\n : undefined,\n },\n );\n };\n },\n });\n}\n"],"names":["cloudflareAccessSignInResolvers","createSignInResolverFactory","z"],"mappings":";;;;;AA4BiBA;AAAA,CAAV,CAAUA,gCAAV,KAAA;AAIE,EAAMA,gCAAAA,CAAA,sCACXC,0CAA4B,CAAA;AAAA,IAC1B,aAAA,EAAeC,MACZ,MAAO,CAAA;AAAA,MACN,0CAA4C,EAAAA,KAAA,CAAE,OAAQ,EAAA,CAAE,QAAS;AAAA,KAClE,EACA,QAAS,EAAA;AAAA,IACZ,MAAA,CAAO,OAAU,GAAA,EAAI,EAAA;AACnB,MAAO,OAAA,OAAO,MAA0C,GAAQ,KAAA;AAC9D,QAAM,MAAA,EAAE,SAAY,GAAA,IAAA;AAEpB,QAAI,IAAA,CAAC,QAAQ,KAAO,EAAA;AAClB,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA;AAGF,QAAA,OAAO,GAAI,CAAA,qBAAA;AAAA,UACT;AAAA,YACE,MAAQ,EAAA;AAAA,cACN,sBAAsB,OAAQ,CAAA;AAAA;AAChC,WACF;AAAA,UACA;AAAA,YACE,0BAAA,EACE,OAAS,EAAA,0CAAA,GACL,EAAE,SAAA,EAAW,EAAE,IAAM,EAAA,OAAA,CAAQ,KAAM,EAAA,EACnC,GAAA,KAAA;AAAA;AACR,SACF;AAAA,OACF;AAAA;AACF,GACD,CAAA;AAAA,CApCY,EAAAF,uCAAA,KAAAA,uCAAA,GAAA,EAAA,CAAA,CAAA;;"}
1
+ {"version":3,"file":"resolvers.cjs.js","sources":["../src/resolvers.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n createSignInResolverFactory,\n SignInInfo,\n} from '@backstage/plugin-auth-node';\nimport { CloudflareAccessResult } from './types';\nimport { z } from 'zod';\n\n/**\n * Available sign-in resolvers for the Cloudflare Access auth provider.\n *\n * @public\n */\nexport namespace cloudflareAccessSignInResolvers {\n /**\n * Looks up the user by matching their email to the entity email.\n */\n export const emailMatchingUserEntityProfileEmail =\n createSignInResolverFactory({\n optionsSchema: z\n .object({\n dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(),\n })\n .optional(),\n create(options = {}) {\n return async (info: SignInInfo<CloudflareAccessResult>, ctx) => {\n const { profile } = info;\n\n if (!profile.email) {\n throw new Error(\n 'Login failed, user profile does not contain an email',\n );\n }\n\n return ctx.signInWithCatalogUser(\n {\n filter: {\n 'spec.profile.email': profile.email,\n },\n },\n {\n dangerousEntityRefFallback:\n options?.dangerouslyAllowSignInWithoutUserInCatalog\n ? { entityRef: { name: profile.email } }\n : undefined,\n },\n );\n };\n },\n });\n}\n"],"names":["cloudflareAccessSignInResolvers","createSignInResolverFactory","z"],"mappings":";;;;;AA4BiBA;AAAA,CAAV,CAAUA,gCAAAA,KAAV;AAIE,EAAMA,gCAAAA,CAAA,sCACXC,0CAAA,CAA4B;AAAA,IAC1B,aAAA,EAAeC,MACZ,MAAA,CAAO;AAAA,MACN,0CAAA,EAA4CA,KAAA,CAAE,OAAA,EAAQ,CAAE,QAAA;AAAS,KAClE,EACA,QAAA,EAAS;AAAA,IACZ,MAAA,CAAO,OAAA,GAAU,EAAC,EAAG;AACnB,MAAA,OAAO,OAAO,MAA0C,GAAA,KAAQ;AAC9D,QAAA,MAAM,EAAE,SAAQ,GAAI,IAAA;AAEpB,QAAA,IAAI,CAAC,QAAQ,KAAA,EAAO;AAClB,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA,QACF;AAEA,QAAA,OAAO,GAAA,CAAI,qBAAA;AAAA,UACT;AAAA,YACE,MAAA,EAAQ;AAAA,cACN,sBAAsB,OAAA,CAAQ;AAAA;AAChC,WACF;AAAA,UACA;AAAA,YACE,0BAAA,EACE,OAAA,EAAS,0CAAA,GACL,EAAE,SAAA,EAAW,EAAE,IAAA,EAAM,OAAA,CAAQ,KAAA,EAAM,EAAE,GACrC;AAAA;AACR,SACF;AAAA,MACF,CAAA;AAAA,IACF;AAAA,GACD,CAAA;AAAA,CAAA,EApCYF,uCAAA,KAAAA,uCAAA,GAAA,EAAA,CAAA,CAAA;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.cjs.js","sources":["../src/types.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// JWT Web Token definitions are in the URL below\n// https://developers.cloudflare.com/cloudflare-one/identity/users/validating-json/\nexport const CF_JWT_HEADER = 'cf-access-jwt-assertion';\nexport const CF_AUTH_IDENTITY = 'cf-access-authenticated-user-email';\nexport const COOKIE_AUTH_NAME = 'CF_Authorization';\nexport const CACHE_PREFIX = 'providers/cloudflare-access/profile-v1';\n\nexport type ServiceToken = {\n token: string;\n subject: string;\n};\n\n/**\n * Can be used in externally provided auth handler or sign in resolver to\n * enrich user profile for sign-in user entity\n *\n * @public\n */\nexport type CloudflareAccessClaims = {\n /**\n * `aud` identifies the application to which the JWT is issued.\n */\n aud: string[];\n /**\n * `email` contains the email address of the authenticated user.\n */\n email: string;\n /**\n * iat and exp are the issuance and expiration timestamps.\n */\n exp: number;\n iat: number;\n /**\n * `nonce` is the session identifier.\n */\n nonce: string;\n /**\n * `identity_nonce` is available in the Application Token and can be used to\n * query all group membership for a given user.\n */\n identity_nonce: string;\n /**\n * `sub` contains the identifier of the authenticated user.\n */\n sub: string;\n /**\n * `iss` the issuer is the application’s Cloudflare Access Domain URL.\n */\n iss: string;\n /**\n * `custom` contains SAML attributes in the Application Token specified by an\n * administrator in the identity provider configuration.\n */\n custom: string;\n};\n\n/**\n * @public\n */\nexport type CloudflareAccessGroup = {\n /**\n * Group id\n */\n id: string;\n /**\n * Name of group as defined in Cloudflare zero trust dashboard\n */\n name: string;\n /**\n * Access group email address\n */\n email: string;\n};\n\n/**\n * @public\n */\nexport type CloudflareAccessIdentityProfile = {\n id: string;\n name: string;\n email: string;\n groups: CloudflareAccessGroup[];\n};\n\n/**\n * @public\n */\nexport type CloudflareAccessResult = {\n claims: CloudflareAccessClaims;\n cfIdentity: CloudflareAccessIdentityProfile;\n expiresInSeconds?: number;\n token: string;\n};\n"],"names":[],"mappings":";;AAkBO,MAAM,aAAgB,GAAA;AAEtB,MAAM,gBAAmB,GAAA;AACzB,MAAM,YAAe,GAAA;;;;;;"}
1
+ {"version":3,"file":"types.cjs.js","sources":["../src/types.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// JWT Web Token definitions are in the URL below\n// https://developers.cloudflare.com/cloudflare-one/identity/users/validating-json/\nexport const CF_JWT_HEADER = 'cf-access-jwt-assertion';\nexport const CF_AUTH_IDENTITY = 'cf-access-authenticated-user-email';\nexport const COOKIE_AUTH_NAME = 'CF_Authorization';\nexport const CACHE_PREFIX = 'providers/cloudflare-access/profile-v1';\n\nexport type ServiceToken = {\n token: string;\n subject: string;\n};\n\n/**\n * Can be used in externally provided auth handler or sign in resolver to\n * enrich user profile for sign-in user entity\n *\n * @public\n */\nexport type CloudflareAccessClaims = {\n /**\n * `aud` identifies the application to which the JWT is issued.\n */\n aud: string[];\n /**\n * `email` contains the email address of the authenticated user.\n */\n email: string;\n /**\n * iat and exp are the issuance and expiration timestamps.\n */\n exp: number;\n iat: number;\n /**\n * `nonce` is the session identifier.\n */\n nonce: string;\n /**\n * `identity_nonce` is available in the Application Token and can be used to\n * query all group membership for a given user.\n */\n identity_nonce: string;\n /**\n * `sub` contains the identifier of the authenticated user.\n */\n sub: string;\n /**\n * `iss` the issuer is the application’s Cloudflare Access Domain URL.\n */\n iss: string;\n /**\n * `custom` contains SAML attributes in the Application Token specified by an\n * administrator in the identity provider configuration.\n */\n custom: string;\n};\n\n/**\n * @public\n */\nexport type CloudflareAccessGroup = {\n /**\n * Group id\n */\n id: string;\n /**\n * Name of group as defined in Cloudflare zero trust dashboard\n */\n name: string;\n /**\n * Access group email address\n */\n email: string;\n};\n\n/**\n * @public\n */\nexport type CloudflareAccessIdentityProfile = {\n id: string;\n name: string;\n email: string;\n groups: CloudflareAccessGroup[];\n};\n\n/**\n * @public\n */\nexport type CloudflareAccessResult = {\n claims: CloudflareAccessClaims;\n cfIdentity: CloudflareAccessIdentityProfile;\n expiresInSeconds?: number;\n token: string;\n};\n"],"names":[],"mappings":";;AAkBO,MAAM,aAAA,GAAgB;AAEtB,MAAM,gBAAA,GAAmB;AACzB,MAAM,YAAA,GAAe;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-auth-backend-module-cloudflare-access-provider",
3
- "version": "0.4.6-next.0",
3
+ "version": "0.4.7-next.0",
4
4
  "description": "The cloudflare-access-provider backend module for the auth plugin.",
5
5
  "backstage": {
6
6
  "role": "backend-plugin-module",
@@ -37,19 +37,19 @@
37
37
  "test": "backstage-cli package test"
38
38
  },
39
39
  "dependencies": {
40
- "@backstage/backend-plugin-api": "1.4.2-next.0",
40
+ "@backstage/backend-plugin-api": "1.4.3-next.0",
41
41
  "@backstage/config": "1.3.3",
42
42
  "@backstage/errors": "1.2.7",
43
- "@backstage/plugin-auth-node": "0.6.6-next.0",
43
+ "@backstage/plugin-auth-node": "0.6.7-next.0",
44
44
  "express": "^4.18.2",
45
45
  "jose": "^5.0.0",
46
46
  "zod": "^3.22.4"
47
47
  },
48
48
  "devDependencies": {
49
- "@backstage/backend-defaults": "0.11.2-next.0",
50
- "@backstage/backend-test-utils": "1.7.1-next.0",
51
- "@backstage/cli": "0.33.2-next.0",
52
- "@backstage/plugin-auth-backend": "0.25.3-next.0",
49
+ "@backstage/backend-defaults": "0.12.1-next.0",
50
+ "@backstage/backend-test-utils": "1.9.0-next.1",
51
+ "@backstage/cli": "0.34.2-next.1",
52
+ "@backstage/plugin-auth-backend": "0.25.4-next.0",
53
53
  "@backstage/types": "1.2.1",
54
54
  "msw": "^2.0.0",
55
55
  "node-mocks-http": "^1.0.0",