|
7 | 7 | // the dependency points one way only. |
8 | 8 | // --------------------------------------------------------------------------- |
9 | 9 |
|
10 | | -import { Data, Effect, Result, Schema } from "effect"; |
| 10 | +import { Data, Effect, Option, Result, Schema } from "effect"; |
11 | 11 | import { jwtVerify, type JWTVerifyGetKey } from "jose"; |
12 | 12 | import { JWKSInvalid, JWKSTimeout, JWTExpired } from "jose/errors"; |
| 13 | +import { workosAccessTokenOptions } from "../auth/access-token-options"; |
| 14 | + |
| 15 | +const parseIdentityClaims = Schema.decodeUnknownOption( |
| 16 | + Schema.Struct({ |
| 17 | + sub: Schema.NonEmptyString, |
| 18 | + org_id: Schema.optionalKey(Schema.NullOr(Schema.NonEmptyString)), |
| 19 | + }), |
| 20 | +); |
| 21 | + |
| 22 | +const identityFromClaims = (payload: unknown): VerifiedToken | null => |
| 23 | + Option.match(parseIdentityClaims(payload), { |
| 24 | + onNone: () => null, |
| 25 | + onSome: (claims) => ({ |
| 26 | + accountId: claims.sub, |
| 27 | + organizationId: claims.org_id ?? null, |
| 28 | + }), |
| 29 | + }); |
13 | 30 |
|
14 | 31 | export type VerifiedToken = { |
15 | 32 | /** The WorkOS account ID (user ID). */ |
@@ -91,18 +108,14 @@ export const verifyMcpAccessToken = ( |
91 | 108 | const { payload } = yield* Effect.tryPromise({ |
92 | 109 | try: () => |
93 | 110 | jwtVerify(token, jwks, { |
| 111 | + ...workosAccessTokenOptions, |
94 | 112 | issuer: options.issuer, |
95 | 113 | audience: options.audience, |
96 | 114 | }), |
97 | 115 | catch: classifyJwtVerificationError, |
98 | 116 | }).pipe(withJwtVerificationSpan); |
99 | 117 |
|
100 | | - if (!payload.sub) return null; |
101 | | - |
102 | | - return { |
103 | | - accountId: payload.sub, |
104 | | - organizationId: (payload.org_id as string | undefined) ?? null, |
105 | | - } satisfies VerifiedToken; |
| 118 | + return identityFromClaims(payload); |
106 | 119 | }); |
107 | 120 |
|
108 | 121 | export const verifyWorkOSMcpAccessToken = ( |
@@ -134,14 +147,9 @@ export const verifyWorkOSMcpAccessToken = ( |
134 | 147 | export const verifyWorkosUserManagementToken = (token: string, jwks: JWTVerifyGetKey) => |
135 | 148 | Effect.gen(function* () { |
136 | 149 | const { payload } = yield* Effect.tryPromise({ |
137 | | - try: () => jwtVerify(token, jwks), |
| 150 | + try: () => jwtVerify(token, jwks, workosAccessTokenOptions), |
138 | 151 | catch: classifyJwtVerificationError, |
139 | 152 | }).pipe(withJwtVerificationSpan); |
140 | 153 |
|
141 | | - if (!payload.sub) return null; |
142 | | - |
143 | | - return { |
144 | | - accountId: payload.sub, |
145 | | - organizationId: (payload.org_id as string | undefined) ?? null, |
146 | | - } satisfies VerifiedToken; |
| 154 | + return identityFromClaims(payload); |
147 | 155 | }); |
0 commit comments