(
input: OAuthCompleteInput,
)
| 1212 | // complete — redeem the session, exchange the code, mint the connection. |
| 1213 | // ----------------------------------------------------------------------- |
| 1214 | const complete = ( |
| 1215 | input: OAuthCompleteInput, |
| 1216 | ): Effect.Effect<Connection, OAuthCompleteError | OAuthSessionNotFoundError | StorageFailure> => |
| 1217 | Effect.gen(function* () { |
| 1218 | const sessionRow = yield* deps.fuma.use("oauth_session.findFirst", (db) => |
| 1219 | looseDb(db).findFirst("oauth_session", { |
| 1220 | where: (b: any) => b("state", "=", String(input.state)), |
| 1221 | }), |
| 1222 | ); |
| 1223 | if (!sessionRow) { |
| 1224 | return yield* new OAuthSessionNotFoundError({ state: input.state }); |
| 1225 | } |
| 1226 | const session = { |
| 1227 | owner: String(sessionRow.owner) as Owner, |
| 1228 | clientSlug: OAuthClientSlug.make(String(sessionRow.client_slug)), |
| 1229 | integration: IntegrationSlug.make(String(sessionRow.integration)), |
| 1230 | name: ConnectionName.make(String(sessionRow.name)), |
| 1231 | template: AuthTemplateSlug.make(String(sessionRow.template)), |
| 1232 | redirectUrl: String(sessionRow.redirect_url), |
| 1233 | pkceVerifier: sessionRow.pkce_verifier == null ? null : String(sessionRow.pkce_verifier), |
| 1234 | identityLabel: sessionRow.identity_label == null ? null : String(sessionRow.identity_label), |
| 1235 | expiresAt: Number(sessionRow.expires_at), |
| 1236 | // The scope set `start` requested (the integration's declared or |
| 1237 | // discovered scopes), persisted on the session payload. Drives the |
| 1238 | // recorded-scope fallback when the AS omits `scope`. Missing/legacy |
| 1239 | // payloads fall back to the client's scopes below. |
| 1240 | requestedScopes: requestedScopesFromPayload(sessionRow.payload), |
| 1241 | // The app's owner, recorded by `start` — reload the SAME app at |
| 1242 | // completion by explicit owner (no derivation). Defaults to the session |
| 1243 | // owner for same-owner connects. |
| 1244 | clientOwner: |
| 1245 | clientOwnerFromPayload(sessionRow.payload) ?? (String(sessionRow.owner) as Owner), |
| 1246 | }; |
| 1247 | |
| 1248 | // Expired sessions are not redeemable — drop + treat as not found. |
| 1249 | if (Number.isFinite(session.expiresAt) && session.expiresAt <= Date.now()) { |
| 1250 | yield* deleteSession(input.state); |
| 1251 | return yield* new OAuthSessionNotFoundError({ state: input.state }); |
| 1252 | } |
| 1253 | |
| 1254 | // Reload the SAME app `start` resolved, by its explicit recorded owner. |
| 1255 | const client = yield* loadClient(session.clientOwner, session.clientSlug); |
| 1256 | if (!client) { |
| 1257 | return yield* new OAuthCompleteError({ |
| 1258 | message: `OAuth client not found: ${session.clientSlug}`, |
| 1259 | restartRequired: true, |
| 1260 | }); |
| 1261 | } |
| 1262 | |
| 1263 | // The PKCE verifier is minted by `start` for every authorization_code |
| 1264 | // session. A null/missing one means a corrupt session row — exchanging |
| 1265 | // with an empty verifier would violate RFC 7636 and the AS would reject |
| 1266 | // it with an opaque error. Fail loudly + require a restart instead. |
| 1267 | if (session.pkceVerifier == null) { |
| 1268 | return yield* new OAuthCompleteError({ |
| 1269 | message: `OAuth session ${input.state} is missing its PKCE code verifier; restart the flow.`, |
| 1270 | restartRequired: true, |
| 1271 | }); |
nothing calls this directly
no test coverage detected