diff --git a/packages/schemas/alterations/next-1713714446-add-cloud-api-scope-invoke-custom-jwt-workers.ts b/packages/schemas/alterations/next-1713714446-add-cloud-api-scope-invoke-custom-jwt-workers.ts new file mode 100644 index 000000000..7e7a5ec41 --- /dev/null +++ b/packages/schemas/alterations/next-1713714446-add-cloud-api-scope-invoke-custom-jwt-workers.ts @@ -0,0 +1,58 @@ +import { generateStandardId } from '@logto/shared/universal'; +import { sql } from '@silverhand/slonik'; + +import type { AlterationScript } from '../lib/types/alteration.js'; + +type Resource = { + tenantId: string; + id: string; + name: string; + indicator: string; +}; + +const cloudApiIndicator = 'https://cloud.logto.io/api'; + +const adminTenantId = 'admin'; + +const invokeCustomJwtWorkersCloudScopeName = 'invoke:custom:jwt:workers'; +const invokeCustomJwtWorkersCloudScopeDescription = + 'Allow accessing custom JWT workers to fetch the parsed token payload.'; + +const alteration: AlterationScript = { + up: async (pool) => { + // Get the Cloud API resource + const cloudApiResource = await pool.one(sql` + select * from resources + where tenant_id = ${adminTenantId} + and indicator = ${cloudApiIndicator} + `); + + // Create the `invoke:custom:jwt:workers` scope + await pool.query(sql` + insert into scopes (id, tenant_id, resource_id, name, description) + values (${generateStandardId()}, ${adminTenantId}, ${ + cloudApiResource.id + }, ${invokeCustomJwtWorkersCloudScopeName}, ${invokeCustomJwtWorkersCloudScopeDescription}); + `); + }, + down: async (pool) => { + // Get the Cloud API resource + const cloudApiResource = await pool.one(sql` + select * from resources + where tenant_id = ${adminTenantId} + and indicator = ${cloudApiIndicator} + `); + + // Remove the `invoke:custom:jwt:workers` scope + await pool.query(sql` + delete from scopes + where + tenant_id = ${adminTenantId} and + name = ${invokeCustomJwtWorkersCloudScopeName} and + description = ${invokeCustomJwtWorkersCloudScopeDescription} and + resource_id = ${cloudApiResource.id} + `); + }, +}; + +export default alteration; diff --git a/packages/schemas/src/seeds/cloud-api.ts b/packages/schemas/src/seeds/cloud-api.ts index 30782d563..afa5cf0b7 100644 --- a/packages/schemas/src/seeds/cloud-api.ts +++ b/packages/schemas/src/seeds/cloud-api.ts @@ -22,6 +22,16 @@ export enum CloudScope { * scripts and fetch the parsed token payload. */ FetchCustomJwt = 'fetch:custom:jwt', + /** + * From current design, we have two different ways to execute JWT customizer scripts: + * with Azure Functions (for Dev tenants) and with Cloudflare Workers (for Pro tenants). + * + * In order to secure the Cloudflare Workers (they are publicly accessible), we decide to use Logto's internal M2M + * mechanism to protect the Workers. + * + * The entity (this is designed to be a M2M application scope) can invoke Cloudflare Workers to fetch custom JWT result. + */ + InvokeCustomJwtWorkers = 'invoke:custom:jwt:workers', /** The user can see and manage affiliates, including create, update, and delete. */ ManageAffiliate = 'manage:affiliate', /** The user can create new affiliates and logs. */ @@ -70,6 +80,10 @@ export const createCloudApi = (): Readonly<[UpdateAdminData, ...CreateScope[]]> CloudScope.FetchCustomJwt, 'Allow accessing external resource to execute JWT payload customizer script and fetch the parsed token payload.' ), + buildScope( + CloudScope.InvokeCustomJwtWorkers, + 'Allow accessing custom JWT workers to fetch the parsed token payload.' + ), buildScope(CloudScope.CreateAffiliate, 'Allow creating new affiliates and logs.'), buildScope( CloudScope.ManageAffiliate,