Plan — BJ-1: AtlassianOAuthAuthProvider abstract base#
Summary#
Add an abstract NestJS provider that models the PlatformAuthProvider interface using the OAuth 2.0 refresh-token flow (rotating refresh tokens). It is the shared auth foundation both Bitbucket and Jira providers (BJ-2, BJ-3) will extend.
Files#
| Path | Action | Purpose |
|---|---|---|
src/atlassian/atlassian-oauth-auth.provider.ts | create | Abstract class implementing PlatformAuthProvider with refresh-token flow + concurrency guard. |
src/atlassian/atlassian-oauth-state.repo.ts | create | Minimal typed interface + injection token for the state repo (actual impl lands in BJ-5). |
src/atlassian/index.ts | create | Barrel exporting the abstract class, state repo interface, and token. |
src/atlassian/atlassian-oauth-auth.provider.spec.ts | create | Unit tests via a concrete test subclass; injected HTTP client + repo mock. |
Steps#
- Define
AtlassianOAuthStateRepointerface (loadRefreshToken(platform: string): Promise<string | null>,saveRefreshToken(platform: string, token: string): Promise<void>) plusATLASSIAN_OAUTH_STATE_REPOinjection token inatlassian-oauth-state.repo.ts. - Implement abstract
AtlassianOAuthAuthProviderinatlassian-oauth-auth.provider.ts: constructor takes subclass config{ tokenEndpoint, clientId, clientSecret, refreshTokenBootstrap, platform }, injectedAtlassianOAuthStateRepo, and optionalfetchFn(defaults to globalfetch) for testability. - Implement
getToken()— return cached access token when> 5 minfrom expiry; otherwise run refresh through a single in-flightrefreshPromise(copy the guard pattern fromsrc/github/github-app-auth.provider.ts:60-70). Do not overwrite cache on error. - Implement
refreshAccessToken()—POST <tokenEndpoint>form-encodedgrant_type=refresh_token&refresh_token=<current>&client_id=<id>&client_secret=<secret>; on success persist the rotatedrefresh_tokenviastateRepo.saveRefreshToken(platform, ...)before returning access token. - Add abstract
getBotUsername(): stringand JSDoc describing the refresh-token rotation invariant (Atlassian rotates on each refresh; persistence must happen before returning). - Create
src/atlassian/index.tsbarrel re-exporting the abstract class, interface, and token. - Add
atlassian-oauth-auth.provider.spec.tswith a minimal concrete subclass exercising: cached token reuse, preemptive refresh within 5-min buffer, concurrentgetToken()calls share one refresh, refresh persists rotated token, endpoint failure propagates without corrupting state.
Verification#
npm run buildpasses (tsc strict, abstract class conforms toPlatformAuthProvider).npm run test -- atlassian-oauth-authpasses all new spec cases (happy, concurrency, rotation persistence, error).npm run lintzero warnings.
Risks#
AtlassianOAuthStateRepohere is an interface only; BJ-5 supplies the impl. Consumers (BJ-2/BJ-3) must not instantiate the abstract class until a real repo provider is registered.
Assumptions#
- Target base branch is
rc/atlassian-integration(per issue), which already contains BJ-0 (PlatformAuthProviderinterface + GitHub auth providers from #540). - Atlassian token endpoint accepts standard
application/x-www-form-urlencodedOAuth 2.0 refresh-token grant and returns{ access_token, expires_in, refresh_token }.