@knpkv/atlassian-common 1.5.0 → 1.7.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 +20 -0
- package/README.md +31 -0
- package/dist/cli-auth/AtlassianCliAuth.d.ts +110 -0
- package/dist/cli-auth/AtlassianCliAuth.d.ts.map +1 -0
- package/dist/cli-auth/AtlassianCliAuth.js +298 -0
- package/dist/cli-auth/AtlassianCliAuth.js.map +1 -0
- package/dist/cli-auth/authCommand.d.ts +34 -0
- package/dist/cli-auth/authCommand.d.ts.map +1 -0
- package/dist/cli-auth/authCommand.js +127 -0
- package/dist/cli-auth/authCommand.js.map +1 -0
- package/dist/cli-auth/index.d.ts +14 -0
- package/dist/cli-auth/index.d.ts.map +1 -0
- package/dist/cli-auth/index.js +14 -0
- package/dist/cli-auth/index.js.map +1 -0
- package/dist/cli-auth/internal/NodeLayers.d.ts +5 -0
- package/dist/cli-auth/internal/NodeLayers.d.ts.map +1 -0
- package/dist/cli-auth/internal/NodeLayers.js +17 -0
- package/dist/cli-auth/internal/NodeLayers.js.map +1 -0
- package/dist/cli-auth/internal/oauthServer.d.ts +54 -0
- package/dist/cli-auth/internal/oauthServer.d.ts.map +1 -0
- package/dist/cli-auth/internal/oauthServer.js +94 -0
- package/dist/cli-auth/internal/oauthServer.js.map +1 -0
- package/dist/cli-auth/openBrowser.d.ts +16 -0
- package/dist/cli-auth/openBrowser.d.ts.map +1 -0
- package/dist/cli-auth/openBrowser.js +30 -0
- package/dist/cli-auth/openBrowser.js.map +1 -0
- package/package.json +5 -1
- package/src/cli-auth/AtlassianCliAuth.ts +539 -0
- package/src/cli-auth/authCommand.ts +227 -0
- package/src/cli-auth/index.ts +20 -0
- package/src/cli-auth/internal/NodeLayers.ts +25 -0
- package/src/cli-auth/internal/oauthServer.ts +168 -0
- package/src/cli-auth/openBrowser.ts +47 -0
- package/test/ApiUpdateWorkflows.test.ts +78 -0
- package/test/cliAuth.authCommand.test.ts +153 -0
- package/test/cliAuth.guardrails.test.ts +76 -0
- package/test/cliAuth.login.test.ts +319 -0
- package/test/cliAuth.oauthServer.test.ts +276 -0
- package/test/cliAuth.openBrowser.test.ts +70 -0
- package/test/cliAuth.refresh.test.ts +304 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @knpkv/atlassian-common
|
|
2
2
|
|
|
3
|
+
## 1.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#472](https://github.com/knpkv/npm/pull/472) [`d12c336`](https://github.com/knpkv/npm/commit/d12c3364e56abb7cfd32aa693c5406788a67e3f5) Thanks [@konopkov](https://github.com/konopkov)! - One shared `auth` command group for jira and confluence: `makeAuthCommand(service, descriptor, options)` in `@knpkv/atlassian-common/cli-auth`. Each CLI exports one descriptor (`jiraCliDescriptor`, `confluenceCliDescriptor`: command name, product name, login scopes) that feeds both its auth and its `auth` commands.
|
|
8
|
+
|
|
9
|
+
- `jira auth create` lists every scope `jira auth login` requests. It previously listed three of five (missing `write:jira-work` and `manage:jira-project`), so an app set up from its instructions could not log in.
|
|
10
|
+
- `jira auth manage` is new: it opens the developer console with the scope and callback checklist.
|
|
11
|
+
- `confluence auth profiles`, `auth use <profile>` and `auth remove <profile>` are new; the docs already described them.
|
|
12
|
+
- `confluence auth status` prints the active profile, account, site and profile ID.
|
|
13
|
+
- `auth create` and `auth manage` print the URL first and exit non-zero when no browser opens (jira's `create` used to ignore that).
|
|
14
|
+
|
|
15
|
+
## 1.6.0
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- [#462](https://github.com/knpkv/npm/pull/462) [`a5490aa`](https://github.com/knpkv/npm/commit/a5490aad738507f2a3a7d7cb4babf3545d287087) Thanks [@konopkov](https://github.com/konopkov)! - `@knpkv/atlassian-common/cli-auth` now holds the browser login and rotating refresh-token flow that `jira` and `confluence` each carried a copy of; both CLIs bind it with `makeAtlassianCliAuth`. Login no longer gives up when no browser opens — it says so on stderr and keeps waiting, since the URL is already printed (`jira` used to treat a launcher that ran but failed as success; `confluence` aborted the login). `jira` login also reports the provider's error code when its description is empty, and reports callback-server failures as `OAuthError`.
|
|
20
|
+
|
|
21
|
+
Jira Clockify's timeout documentation now points to the shared refresh implementation.
|
|
22
|
+
|
|
3
23
|
## 1.5.0
|
|
4
24
|
|
|
5
25
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ pnpm add @knpkv/atlassian-common
|
|
|
15
15
|
| `@knpkv/atlassian-common` | Everything (AST, auth, config, serializers) |
|
|
16
16
|
| `@knpkv/atlassian-common/ast` | Inline, block, macro, and document AST nodes |
|
|
17
17
|
| `@knpkv/atlassian-common/auth` | OAuth2 endpoints, PKCE, token exchange |
|
|
18
|
+
| `@knpkv/atlassian-common/cli-auth` | Browser login and token refresh for a CLI |
|
|
18
19
|
| `@knpkv/atlassian-common/config` | Config paths, schemas, token/profile storage |
|
|
19
20
|
| `@knpkv/atlassian-common/serializers` | Markdown serializer for AST nodes |
|
|
20
21
|
|
|
@@ -66,6 +67,36 @@ read/identity scopes required for create-only project-version publication, but e
|
|
|
66
67
|
`write:jira-work` and `manage:jira-configuration`. Jira issue edits remain proposal-only in Control
|
|
67
68
|
Center and must not be presented as atomic Jira writes.
|
|
68
69
|
|
|
70
|
+
## CLI Login And Token Refresh
|
|
71
|
+
|
|
72
|
+
`@knpkv/atlassian-common/cli-auth` is the whole OAuth flow an Atlassian command-line
|
|
73
|
+
tool needs: browser login through a local callback server, the rotating refresh token
|
|
74
|
+
(one refresh at a time, grant and save atomic, stored token deleted only on an
|
|
75
|
+
`invalid_grant` 400/403), and profile switching. A CLI binds it to its own storage
|
|
76
|
+
namespace, scopes and names, and keeps its own service tag:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { makeAtlassianCliAuth, NodeCliAuthLive } from "@knpkv/atlassian-common/cli-auth"
|
|
80
|
+
import { Layer } from "effect"
|
|
81
|
+
|
|
82
|
+
export const layer = Layer.effect(
|
|
83
|
+
JiraAuth,
|
|
84
|
+
makeAtlassianCliAuth({
|
|
85
|
+
toolName: "jira-cli", // storage namespace
|
|
86
|
+
commandName: "jira", // used in "Run 'jira auth login'"
|
|
87
|
+
productName: "Jira", // used in "No Jira sites found"
|
|
88
|
+
scopes: JIRA_CLI_SCOPES,
|
|
89
|
+
authMissing: () => new AuthMissingError()
|
|
90
|
+
})
|
|
91
|
+
).pipe(Layer.provide(NodeCliAuthLive))
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`NodeCliAuthLive` supplies the callback server and token storage on Node; the layer
|
|
95
|
+
still needs `HttpClient`, `ChildProcessSpawner` and `Crypto`. Login prints the
|
|
96
|
+
authorization URL, then tries `open`, `xdg-open` and `rundll32.exe`; if none succeeds
|
|
97
|
+
it says so on stderr and keeps waiting for the callback. It is a separate subpath from
|
|
98
|
+
`./auth` because it carries a Node HTTP server and a browser launcher.
|
|
99
|
+
|
|
69
100
|
## Token And Profile Storage
|
|
70
101
|
|
|
71
102
|
Stores OAuth config, auth profiles, and the active token mirror in `~/.config/atlassian/<tool>/` with 0600 permissions.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as Crypto from "effect/Crypto";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as FileSystem from "effect/FileSystem";
|
|
4
|
+
import * as HttpClient from "effect/http/HttpClient";
|
|
5
|
+
import * as Path from "effect/Path";
|
|
6
|
+
import type * as PlatformError from "effect/PlatformError";
|
|
7
|
+
import { ChildProcessSpawner } from "effect/process";
|
|
8
|
+
import * as Redacted from "effect/Redacted";
|
|
9
|
+
import { OAuthError } from "../auth/OAuthErrors.js";
|
|
10
|
+
import type { AuthProfile } from "../config/AuthProfiles.js";
|
|
11
|
+
import type { HomeDirectoryError } from "../config/ConfigPaths.js";
|
|
12
|
+
import { HomeDirectoryTag } from "../config/ConfigPaths.js";
|
|
13
|
+
import type { OAuthConfig, OAuthUser } from "../config/OAuthSchemas.js";
|
|
14
|
+
import type { FileSystemError } from "../config/TokenStorage.js";
|
|
15
|
+
import { HttpServerFactoryTag } from "./internal/oauthServer.js";
|
|
16
|
+
type StorageError = FileSystemError | HomeDirectoryError | PlatformError.PlatformError;
|
|
17
|
+
/**
|
|
18
|
+
* What distinguishes one Atlassian CLI's auth from another's.
|
|
19
|
+
*
|
|
20
|
+
* @category Types
|
|
21
|
+
*/
|
|
22
|
+
export interface AtlassianCliAuthOptions<MissingError> {
|
|
23
|
+
/** Storage namespace under `~/.config/atlassian/` (e.g. `"jira-cli"`). */
|
|
24
|
+
readonly toolName: string;
|
|
25
|
+
/** Binary name used in hints such as "Run 'jira auth login'". */
|
|
26
|
+
readonly commandName: string;
|
|
27
|
+
/** Product name used in "No Jira sites found for this account". */
|
|
28
|
+
readonly productName: string;
|
|
29
|
+
/** OAuth scopes requested at login. */
|
|
30
|
+
readonly scopes: ReadonlyArray<string>;
|
|
31
|
+
/** The CLI's own "not logged in" error, so callers keep catching their tag. */
|
|
32
|
+
readonly authMissing: () => MissingError;
|
|
33
|
+
/**
|
|
34
|
+
* Consulted only when the tool has no stored OAuth config; a config it returns
|
|
35
|
+
* is saved under {@link toolName}, migrating it.
|
|
36
|
+
*/
|
|
37
|
+
readonly legacyOAuthConfig?: Effect.Effect<OAuthConfig | null, FileSystemError | HomeDirectoryError, FileSystem.FileSystem | Path.Path | HomeDirectoryTag>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Options for the login method.
|
|
41
|
+
*
|
|
42
|
+
* @category Types
|
|
43
|
+
*/
|
|
44
|
+
export interface LoginOptions {
|
|
45
|
+
/** Site URL to select (for accounts with multiple sites) */
|
|
46
|
+
readonly siteUrl?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Information about an accessible Atlassian site.
|
|
50
|
+
*
|
|
51
|
+
* @category Types
|
|
52
|
+
*/
|
|
53
|
+
export interface AccessibleSite {
|
|
54
|
+
readonly id: string;
|
|
55
|
+
readonly name: string;
|
|
56
|
+
readonly url: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Who an Atlassian CLI is: the part of {@link AtlassianCliAuthOptions} its `auth` commands also read.
|
|
60
|
+
* A CLI defines it once and passes the same value to both, so the setup text lists exactly the
|
|
61
|
+
* scopes `login` requests.
|
|
62
|
+
*
|
|
63
|
+
* @category Types
|
|
64
|
+
*/
|
|
65
|
+
export type AtlassianCliDescriptor = Pick<AtlassianCliAuthOptions<never>, "commandName" | "productName" | "scopes">;
|
|
66
|
+
/**
|
|
67
|
+
* The auth operations an Atlassian CLI exposes.
|
|
68
|
+
*
|
|
69
|
+
* @category Types
|
|
70
|
+
*/
|
|
71
|
+
export interface AtlassianCliAuth<MissingError> {
|
|
72
|
+
/** Configure OAuth client credentials */
|
|
73
|
+
readonly configure: (config: OAuthConfig) => Effect.Effect<void, StorageError>;
|
|
74
|
+
/** Check if OAuth is configured */
|
|
75
|
+
readonly isConfigured: () => Effect.Effect<boolean, StorageError>;
|
|
76
|
+
/** Start OAuth login flow. Returns list of sites if multiple are available and none was chosen. */
|
|
77
|
+
readonly login: (options?: LoginOptions) => Effect.Effect<ReadonlyArray<AccessibleSite> | void, OAuthError | StorageError>;
|
|
78
|
+
/** Revoke (best effort) and remove stored authentication */
|
|
79
|
+
readonly logout: () => Effect.Effect<void, OAuthError | StorageError>;
|
|
80
|
+
/** Get access token, refreshing if needed */
|
|
81
|
+
readonly getAccessToken: () => Effect.Effect<Redacted.Redacted<string>, MissingError | OAuthError | StorageError>;
|
|
82
|
+
/** Get cloud ID from stored token */
|
|
83
|
+
readonly getCloudId: () => Effect.Effect<string, MissingError | StorageError>;
|
|
84
|
+
/** Get site URL from stored token */
|
|
85
|
+
readonly getSiteUrl: () => Effect.Effect<string, MissingError | StorageError>;
|
|
86
|
+
/** Get current user info from stored token */
|
|
87
|
+
readonly getCurrentUser: () => Effect.Effect<OAuthUser | null, StorageError>;
|
|
88
|
+
/** Get active auth profile */
|
|
89
|
+
readonly getActiveProfile: () => Effect.Effect<AuthProfile | null, StorageError>;
|
|
90
|
+
/** List stored auth profiles */
|
|
91
|
+
readonly listProfiles: () => Effect.Effect<ReadonlyArray<AuthProfile>, StorageError>;
|
|
92
|
+
/** Switch active profile by ID, name, site URL, cloud ID, or account ID */
|
|
93
|
+
readonly switchProfile: (selector: string) => Effect.Effect<AuthProfile | null, StorageError>;
|
|
94
|
+
/** Remove stored profile by ID, name, site URL, cloud ID, or account ID */
|
|
95
|
+
readonly removeProfile: (selector: string) => Effect.Effect<AuthProfile | null, StorageError>;
|
|
96
|
+
/** Check if user is logged in */
|
|
97
|
+
readonly isLoggedIn: () => Effect.Effect<boolean, StorageError>;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Build an Atlassian CLI's auth operations.
|
|
101
|
+
*
|
|
102
|
+
* Requires an `HttpClient`, a `ChildProcessSpawner` (browser launch), `Crypto`
|
|
103
|
+
* (PKCE), the callback-server factory, and the file system and home directory the
|
|
104
|
+
* tokens are stored under. `NodeCliAuthLive` supplies the last three on Node.
|
|
105
|
+
*
|
|
106
|
+
* @category Constructors
|
|
107
|
+
*/
|
|
108
|
+
export declare const makeAtlassianCliAuth: <MissingError>(options: AtlassianCliAuthOptions<MissingError>) => Effect.Effect<AtlassianCliAuth<MissingError>, never, ChildProcessSpawner.ChildProcessSpawner | Crypto.Crypto | FileSystem.FileSystem | HomeDirectoryTag | HttpClient.HttpClient | HttpServerFactoryTag | Path.Path>;
|
|
109
|
+
export {};
|
|
110
|
+
//# sourceMappingURL=AtlassianCliAuth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AtlassianCliAuth.d.ts","sourceRoot":"","sources":["../../src/cli-auth/AtlassianCliAuth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth2 login, token refresh and profile management for an Atlassian CLI.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **One flow, many CLIs**: `jira` and `confluence` differ only in the storage
|
|
7
|
+
* namespace, scopes, the names in their messages and their "not logged in"
|
|
8
|
+
* error. {@link makeAtlassianCliAuth} takes those as options; each CLI keeps
|
|
9
|
+
* its own service tag and public interface and delegates to this.
|
|
10
|
+
* - **Refresh lock**: a `Ref<Option<Deferred>>` prevents concurrent refreshes —
|
|
11
|
+
* the first caller refreshes, the others await the same Deferred.
|
|
12
|
+
* - **Browser-based login**: starts a local callback server, opens the browser,
|
|
13
|
+
* and awaits the OAuth code with a 5-minute timeout. A browser that will not
|
|
14
|
+
* open is reported on stderr and the wait continues: the URL is printed first.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const layer = Layer.effect(
|
|
19
|
+
* JiraAuth,
|
|
20
|
+
* makeAtlassianCliAuth({ toolName: "jira-cli", commandName: "jira", productName: "Jira", scopes, authMissing })
|
|
21
|
+
* ).pipe(Layer.provide(NodeCliAuthLive))
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @module
|
|
25
|
+
*/
|
|
26
|
+
import * as Clock from "effect/Clock";
|
|
27
|
+
import * as Console from "effect/Console";
|
|
28
|
+
import * as Crypto from "effect/Crypto";
|
|
29
|
+
import * as Deferred from "effect/Deferred";
|
|
30
|
+
import * as Effect from "effect/Effect";
|
|
31
|
+
import * as FileSystem from "effect/FileSystem";
|
|
32
|
+
import * as HttpClient from "effect/http/HttpClient";
|
|
33
|
+
import * as Option from "effect/Option";
|
|
34
|
+
import * as Path from "effect/Path";
|
|
35
|
+
import { ChildProcessSpawner } from "effect/process";
|
|
36
|
+
import * as Redacted from "effect/Redacted";
|
|
37
|
+
import * as Ref from "effect/Ref";
|
|
38
|
+
import { buildAuthUrl, computeCodeChallenge, generateCodeVerifier } from "../auth/OAuthEndpoints.js";
|
|
39
|
+
import { OAuthError } from "../auth/OAuthErrors.js";
|
|
40
|
+
import { buildOAuthTokenAt, exchangeCodeForTokens, getAccessibleResources, getUserInfo, refreshToken, revokeToken } from "../auth/OAuthOperations.js";
|
|
41
|
+
import { generateUUID } from "../auth/uuid.js";
|
|
42
|
+
import { deleteActiveProfile, deleteProfileBySelector, loadActiveProfile, loadActiveProfileToken, loadProfiles, saveProfileToken, setActiveProfileBySelector } from "../config/AuthProfiles.js";
|
|
43
|
+
import { HomeDirectoryTag } from "../config/ConfigPaths.js";
|
|
44
|
+
import { isTokenExpiredAt, loadOAuthConfig, saveOAuthConfig } from "../config/TokenStorage.js";
|
|
45
|
+
import { callbackUrl, HttpServerFactoryTag, startCallbackServer } from "./internal/oauthServer.js";
|
|
46
|
+
import { openBrowser } from "./openBrowser.js";
|
|
47
|
+
// Atlassian rotates refresh tokens: the response carries a replacement and the
|
|
48
|
+
// one we sent is consumed server-side. Interrupting between the round-trip and
|
|
49
|
+
// the save therefore destroys the credential — the next refresh fails and
|
|
50
|
+
// `getAccessToken` reacts by deleting the token file, so the user is silently
|
|
51
|
+
// logged out and has to log in again.
|
|
52
|
+
//
|
|
53
|
+
// Interruption is routine here, not hypothetical: this runs during layer
|
|
54
|
+
// construction on every CLI invocation, and jcf's nvim statusline kills the
|
|
55
|
+
// process on `VimLeave`. So the grant and the persist are atomic.
|
|
56
|
+
//
|
|
57
|
+
// The region carries its own deadline rather than relying on a caller's.
|
|
58
|
+
// `Effect.timeout` is a race, and racing an uninterruptible loser means waiting
|
|
59
|
+
// for it — an outer bound would go inert and, worse, an uninterruptible region
|
|
60
|
+
// with no deadline of its own absorbs SIGINT/SIGTERM entirely
|
|
61
|
+
// (`NodeRuntime.runMain`'s signal handlers only interrupt the main fiber),
|
|
62
|
+
// leaving a process that ignores Ctrl-C. The deadline forked inside the region
|
|
63
|
+
// is itself interruptible, so it does bound this.
|
|
64
|
+
//
|
|
65
|
+
// Abandoning the round-trip cannot prove the grant did not land: Atlassian may
|
|
66
|
+
// consume the refresh token and rotate it after we have stopped listening, and
|
|
67
|
+
// no client-side deadline changes that. So the deadline is paired with the rule
|
|
68
|
+
// in `getAccessToken` — a refresh that fails without a verdict never deletes the
|
|
69
|
+
// stored token. The credential survives to be retried, and only a real
|
|
70
|
+
// rejection ends the session.
|
|
71
|
+
const REFRESH_TIMEOUT = "30 seconds";
|
|
72
|
+
/**
|
|
73
|
+
* Build an Atlassian CLI's auth operations.
|
|
74
|
+
*
|
|
75
|
+
* Requires an `HttpClient`, a `ChildProcessSpawner` (browser launch), `Crypto`
|
|
76
|
+
* (PKCE), the callback-server factory, and the file system and home directory the
|
|
77
|
+
* tokens are stored under. `NodeCliAuthLive` supplies the last three on Node.
|
|
78
|
+
*
|
|
79
|
+
* @category Constructors
|
|
80
|
+
*/
|
|
81
|
+
export const makeAtlassianCliAuth = Effect.fn("AtlassianCliAuth.make")((options) => Effect.gen(function* () {
|
|
82
|
+
const { authMissing, commandName, productName, scopes, toolName } = options;
|
|
83
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
84
|
+
const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
85
|
+
const cryptoService = yield* Crypto.Crypto;
|
|
86
|
+
const serverFactory = yield* HttpServerFactoryTag;
|
|
87
|
+
const withHttp = Effect.provideService(HttpClient.HttpClient, httpClient);
|
|
88
|
+
const withCrypto = Effect.provideService(Crypto.Crypto, cryptoService);
|
|
89
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
90
|
+
const path = yield* Path.Path;
|
|
91
|
+
const homeDirectory = yield* HomeDirectoryTag;
|
|
92
|
+
const withStorage = (effect) => effect.pipe(Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, path), Effect.provideService(HomeDirectoryTag, homeDirectory));
|
|
93
|
+
const loadToken = () => loadActiveProfileToken(toolName).pipe(withStorage);
|
|
94
|
+
const saveToken = (token) => saveProfileToken(toolName, token).pipe(withStorage);
|
|
95
|
+
const deleteToken = () => deleteActiveProfile(toolName).pipe(withStorage);
|
|
96
|
+
const saveConfig = (config) => saveOAuthConfig(toolName, config).pipe(withStorage);
|
|
97
|
+
const legacyOAuthConfig = options.legacyOAuthConfig;
|
|
98
|
+
const loadConfig = Effect.fn("AtlassianCliAuth.loadConfig")(() => Effect.gen(function* () {
|
|
99
|
+
const config = yield* loadOAuthConfig(toolName);
|
|
100
|
+
if (config !== null || legacyOAuthConfig === undefined)
|
|
101
|
+
return config;
|
|
102
|
+
const legacy = yield* legacyOAuthConfig;
|
|
103
|
+
if (legacy !== null) {
|
|
104
|
+
yield* saveOAuthConfig(toolName, legacy);
|
|
105
|
+
}
|
|
106
|
+
return legacy;
|
|
107
|
+
}).pipe(withStorage));
|
|
108
|
+
const refreshLock = yield* Ref.make(Option.none());
|
|
109
|
+
// The URL is printed before this runs, so a browser that will not open is
|
|
110
|
+
// reported and the wait for the callback continues.
|
|
111
|
+
const openBrowserOrWarn = Effect.fn("AtlassianCliAuth.openBrowserOrWarn")((url) => openBrowser(url).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, childProcessSpawner), Effect.catchTags({
|
|
112
|
+
BrowserOpenError: (error) => Console.error(`Could not open a browser (${error.command} exited ${error.exitCode}); visit the URL above.`),
|
|
113
|
+
PlatformError: () => Console.error("Could not open a browser; visit the URL above.")
|
|
114
|
+
})));
|
|
115
|
+
const getConfig = Effect.fn("AtlassianCliAuth.getConfig")(() => Effect.gen(function* () {
|
|
116
|
+
const config = yield* loadConfig();
|
|
117
|
+
if (config === null) {
|
|
118
|
+
return yield* new OAuthError({
|
|
119
|
+
step: "authorize",
|
|
120
|
+
cause: `OAuth not configured. Run '${commandName} auth configure' first.`
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
return config;
|
|
124
|
+
}));
|
|
125
|
+
const refreshAndPersist = Effect.fn("AtlassianCliAuth.refreshAndPersist")((token, config) => Effect.uninterruptible(Effect.gen(function* () {
|
|
126
|
+
const updated = yield* refreshToken(token, config).pipe(withHttp, Effect.timeout(REFRESH_TIMEOUT), Effect.catchTag("TimeoutError", () => Effect.fail(new OAuthError({ step: "refresh", cause: `no response within ${REFRESH_TIMEOUT}` }))));
|
|
127
|
+
yield* saveToken(updated);
|
|
128
|
+
return updated;
|
|
129
|
+
})));
|
|
130
|
+
const isConfigured = Effect.fn("AtlassianCliAuth.isConfigured")(() => loadConfig().pipe(Effect.map((config) => config !== null)));
|
|
131
|
+
const login = Effect.fn("AtlassianCliAuth.login")((loginOptions) => Effect.gen(function* () {
|
|
132
|
+
const config = yield* getConfig();
|
|
133
|
+
const state = yield* generateUUID().pipe(withCrypto);
|
|
134
|
+
const codeVerifier = yield* generateCodeVerifier().pipe(withCrypto);
|
|
135
|
+
const codeChallenge = yield* computeCodeChallenge(codeVerifier).pipe(withCrypto);
|
|
136
|
+
const { code, port } = yield* Effect.scoped(Effect.gen(function* () {
|
|
137
|
+
const { codePromise, port } = yield* startCallbackServer(state).pipe(Effect.provideService(HttpServerFactoryTag, serverFactory));
|
|
138
|
+
const authUrl = buildAuthUrl({
|
|
139
|
+
clientId: config.clientId,
|
|
140
|
+
state,
|
|
141
|
+
port,
|
|
142
|
+
redirectUri: callbackUrl(port),
|
|
143
|
+
scopes: [...scopes],
|
|
144
|
+
codeChallenge
|
|
145
|
+
});
|
|
146
|
+
yield* Console.log(`Opening browser for Atlassian login (callback on port ${port})...`);
|
|
147
|
+
yield* Console.log(`If browser doesn't open, visit: ${authUrl}`);
|
|
148
|
+
yield* openBrowserOrWarn(authUrl);
|
|
149
|
+
yield* Console.log("Waiting for authorization (press Ctrl+C to cancel)...");
|
|
150
|
+
const code = yield* codePromise.pipe(Effect.timeout("5 minutes"), Effect.catchTag("TimeoutError", () => Effect.fail(new OAuthError({ step: "authorize", cause: "Authorization timed out" }))));
|
|
151
|
+
return { code, port };
|
|
152
|
+
}));
|
|
153
|
+
yield* Console.log("Exchanging code for tokens...");
|
|
154
|
+
const tokens = yield* exchangeCodeForTokens(code, config, {
|
|
155
|
+
port,
|
|
156
|
+
redirectUri: callbackUrl(port),
|
|
157
|
+
codeVerifier
|
|
158
|
+
}).pipe(withHttp);
|
|
159
|
+
yield* Console.log("Fetching accessible sites...");
|
|
160
|
+
const sites = yield* getAccessibleResources(tokens.access_token).pipe(withHttp);
|
|
161
|
+
const [first, ...others] = sites;
|
|
162
|
+
if (first === undefined) {
|
|
163
|
+
return yield* new OAuthError({
|
|
164
|
+
step: "authorize",
|
|
165
|
+
cause: `No ${productName} sites found for this account`
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
let site = first;
|
|
169
|
+
if (others.length > 0) {
|
|
170
|
+
const siteUrl = loginOptions?.siteUrl;
|
|
171
|
+
if (siteUrl === undefined || siteUrl === "") {
|
|
172
|
+
yield* Console.log(`Multiple ${productName} sites found. Please select one:`);
|
|
173
|
+
for (const s of sites) {
|
|
174
|
+
yield* Console.log(` - ${s.name}: ${s.url}`);
|
|
175
|
+
}
|
|
176
|
+
yield* Console.log(`\nRun '${commandName} auth login --site <url>' to select a site`);
|
|
177
|
+
return sites.map((s) => ({ id: s.id, name: s.name, url: s.url }));
|
|
178
|
+
}
|
|
179
|
+
const matched = sites.find((s) => s.url === siteUrl);
|
|
180
|
+
if (matched === undefined) {
|
|
181
|
+
const available = sites.map((s) => ` - ${s.name}: ${s.url}`).join("\n");
|
|
182
|
+
return yield* new OAuthError({
|
|
183
|
+
step: "authorize",
|
|
184
|
+
cause: `Site '${siteUrl}' not found. Available sites:\n${available}`
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
site = matched;
|
|
188
|
+
}
|
|
189
|
+
yield* Console.log("Fetching user info...");
|
|
190
|
+
const user = yield* getUserInfo(tokens.access_token).pipe(withHttp);
|
|
191
|
+
const nowMs = yield* Clock.currentTimeMillis;
|
|
192
|
+
yield* saveToken(buildOAuthTokenAt(tokens, site, user, nowMs));
|
|
193
|
+
yield* Console.log(`Logged in as ${user.name} (${user.email})`);
|
|
194
|
+
return undefined;
|
|
195
|
+
}));
|
|
196
|
+
const logout = Effect.fn("AtlassianCliAuth.logout")(() => Effect.gen(function* () {
|
|
197
|
+
const token = yield* loadToken();
|
|
198
|
+
if (token === null) {
|
|
199
|
+
yield* Console.log("Not logged in");
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const config = yield* loadConfig();
|
|
203
|
+
if (config !== null) {
|
|
204
|
+
yield* revokeToken(token, config).pipe(withHttp, Effect.tap(() => Effect.log("Token revoked with Atlassian")), Effect.catch((error) => Effect.log(`Warning: Failed to revoke token: ${error.message}`)));
|
|
205
|
+
}
|
|
206
|
+
yield* deleteToken();
|
|
207
|
+
}));
|
|
208
|
+
const getAccessToken = Effect.fn("AtlassianCliAuth.getAccessToken")(() => Effect.gen(function* () {
|
|
209
|
+
const token = yield* loadToken();
|
|
210
|
+
if (token === null) {
|
|
211
|
+
return yield* Effect.fail(authMissing());
|
|
212
|
+
}
|
|
213
|
+
const nowMs = yield* Clock.currentTimeMillis;
|
|
214
|
+
if (!isTokenExpiredAt(token, nowMs)) {
|
|
215
|
+
return Redacted.make(token.access_token);
|
|
216
|
+
}
|
|
217
|
+
// Atomically check-then-set refresh lock to avoid TOCTOU race
|
|
218
|
+
const deferred = yield* Deferred.make();
|
|
219
|
+
const existing = yield* Ref.modify(refreshLock, (current) => Option.isSome(current)
|
|
220
|
+
? [current.value, current]
|
|
221
|
+
: [deferred, Option.some(deferred)]);
|
|
222
|
+
// Another fiber is already refreshing — just await its result
|
|
223
|
+
if (existing !== deferred) {
|
|
224
|
+
const refreshed = yield* Deferred.await(existing);
|
|
225
|
+
return Redacted.make(refreshed.access_token);
|
|
226
|
+
}
|
|
227
|
+
const refresh = Effect.gen(function* () {
|
|
228
|
+
const config = yield* getConfig();
|
|
229
|
+
// stderr, not stdout: this fires from inside layer construction, so it
|
|
230
|
+
// lands ahead of whatever the command prints. On `--json` that used to
|
|
231
|
+
// put a line of prose in front of the document, and the release
|
|
232
|
+
// automation parsing it failed *after* the remote writes had happened.
|
|
233
|
+
yield* Console.error("Token expired, refreshing...");
|
|
234
|
+
return yield* refreshAndPersist(token, config);
|
|
235
|
+
}).pipe(Effect.catchTag("OAuthError", (error) => {
|
|
236
|
+
// Discard the stored credential only when Atlassian actually rejected
|
|
237
|
+
// it. A refresh can fail without saying anything about the token —
|
|
238
|
+
// transport error, timeout, interruption — and in those cases the
|
|
239
|
+
// grant may even have been consumed server-side, so the one thing we
|
|
240
|
+
// must not do is delete the replacement's only trail. Deleting on any
|
|
241
|
+
// `step === "refresh"` failure turned a flaky network into a silent
|
|
242
|
+
// logout, which is worse than retrying and is unrecoverable.
|
|
243
|
+
//
|
|
244
|
+
// Not every 4xx is a verdict either. `429` is the one this most needs
|
|
245
|
+
// to survive — several `jira`/`jcf` processes on an expired token hit
|
|
246
|
+
// the endpoint together, one wins the rotation and the rest are
|
|
247
|
+
// rate-limited — while `408`/`425` restate the timeout case and `407`
|
|
248
|
+
// and other middlebox replies never came from Atlassian at all.
|
|
249
|
+
//
|
|
250
|
+
// Status alone is still too coarse. `refreshToken` sends the client
|
|
251
|
+
// credentials in the body, so a wrong or rotated `clientSecret` comes
|
|
252
|
+
// back as `400 invalid_client`; deleting the token there destroys a
|
|
253
|
+
// working credential over a config problem, and the re-login it forces
|
|
254
|
+
// would fail the same way until `auth configure` is run. Only
|
|
255
|
+
// `invalid_grant` means the stored token itself is spent, and that is
|
|
256
|
+
// the only thing that ends the session — on a `403` too, since a bare
|
|
257
|
+
// 403 is just as likely to come from a proxy or WAF as from Atlassian
|
|
258
|
+
// revoking anything. An unparseable body leaves `errorCode` absent and
|
|
259
|
+
// the token in place.
|
|
260
|
+
const { errorCode, status } = error;
|
|
261
|
+
const rejected = errorCode === "invalid_grant" && (status === 400 || status === 403);
|
|
262
|
+
if (error.step === "refresh" && rejected) {
|
|
263
|
+
return Effect.gen(function* () {
|
|
264
|
+
yield* deleteToken();
|
|
265
|
+
return yield* new OAuthError({
|
|
266
|
+
step: "refresh",
|
|
267
|
+
cause: `Refresh token expired. Please run '${commandName} auth login' to re-authenticate.`,
|
|
268
|
+
status,
|
|
269
|
+
errorCode
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
return Effect.fail(error);
|
|
274
|
+
}));
|
|
275
|
+
// This fiber owns the refresh. Publish every exit to the waiters —
|
|
276
|
+
// success, failure, and interruption of this fiber alike — so none of
|
|
277
|
+
// them blocks on a result that would otherwise never arrive.
|
|
278
|
+
const result = yield* refresh.pipe(Effect.onExit((exit) => Ref.set(refreshLock, Option.none()).pipe(Effect.andThen(Deferred.done(deferred, exit)))));
|
|
279
|
+
return Redacted.make(result.access_token);
|
|
280
|
+
}));
|
|
281
|
+
const storedTokenField = (read) => () => loadToken().pipe(Effect.flatMap((token) => token === null ? Effect.fail(authMissing()) : Effect.succeed(read(token))));
|
|
282
|
+
return {
|
|
283
|
+
configure: Effect.fn("AtlassianCliAuth.configure")(saveConfig),
|
|
284
|
+
isConfigured,
|
|
285
|
+
login,
|
|
286
|
+
logout,
|
|
287
|
+
getAccessToken,
|
|
288
|
+
getCloudId: Effect.fn("AtlassianCliAuth.getCloudId")(storedTokenField((token) => token.cloud_id)),
|
|
289
|
+
getSiteUrl: Effect.fn("AtlassianCliAuth.getSiteUrl")(storedTokenField((token) => token.site_url)),
|
|
290
|
+
getCurrentUser: Effect.fn("AtlassianCliAuth.getCurrentUser")(() => loadToken().pipe(Effect.map((token) => token?.user ?? null))),
|
|
291
|
+
getActiveProfile: Effect.fn("AtlassianCliAuth.getActiveProfile")(() => loadActiveProfile(toolName).pipe(withStorage)),
|
|
292
|
+
listProfiles: Effect.fn("AtlassianCliAuth.listProfiles")(() => loadProfiles(toolName).pipe(Effect.map((store) => store.profiles), withStorage)),
|
|
293
|
+
switchProfile: Effect.fn("AtlassianCliAuth.switchProfile")((selector) => setActiveProfileBySelector(toolName, selector).pipe(withStorage)),
|
|
294
|
+
removeProfile: Effect.fn("AtlassianCliAuth.removeProfile")((selector) => deleteProfileBySelector(toolName, selector).pipe(withStorage)),
|
|
295
|
+
isLoggedIn: Effect.fn("AtlassianCliAuth.isLoggedIn")(() => loadToken().pipe(Effect.map((token) => token !== null)))
|
|
296
|
+
};
|
|
297
|
+
}));
|
|
298
|
+
//# sourceMappingURL=AtlassianCliAuth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AtlassianCliAuth.js","sourceRoot":"","sources":["../../src/cli-auth/AtlassianCliAuth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `auth` command group every Atlassian CLI exposes, built once over its {@link AtlassianCliAuth}.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **One descriptor, two readers.** The CLI passes the same {@link AtlassianCliDescriptor} it built
|
|
7
|
+
* its auth from, so the setup instructions list exactly the scopes `auth login` requests. A second,
|
|
8
|
+
* hand-written list is how a setup that cannot log in happens: Atlassian rejects an authorization
|
|
9
|
+
* request naming a scope the app lacks.
|
|
10
|
+
* - **Only presentation is configured.** The heading the product's scopes sit under in the
|
|
11
|
+
* developer console, and what to call a site in `--site`.
|
|
12
|
+
* - **A browser that does not open is an error.** The URL is printed first, so a headless or SSH
|
|
13
|
+
* user can still finish by hand; the exit code just stops claiming it worked.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import { Command, Prompt } from "effect/cli";
|
|
18
|
+
import type * as Context from "effect/Context";
|
|
19
|
+
import type { AtlassianCliAuth, AtlassianCliDescriptor } from "./AtlassianCliAuth.js";
|
|
20
|
+
/** The part of an Atlassian CLI's auth the `auth` commands drive. */
|
|
21
|
+
export type AuthCommandService = Pick<AtlassianCliAuth<never>, "configure" | "login" | "logout" | "getActiveProfile" | "listProfiles" | "switchProfile" | "removeProfile">;
|
|
22
|
+
/** What only the product decides about how its `auth` commands read. */
|
|
23
|
+
export interface AuthCommandOptions {
|
|
24
|
+
/** Developer-console heading the product's scopes live under, e.g. `"Jira API"`. */
|
|
25
|
+
readonly apiSection: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The scopes to enable on the OAuth app, by console section. `read:me` is a User Identity scope;
|
|
29
|
+
* `offline_access` is requested at authorize time but is not an app permission, so it is omitted.
|
|
30
|
+
*/
|
|
31
|
+
export declare const scopeInstructions: (descriptor: AtlassianCliDescriptor, apiSection: string) => string;
|
|
32
|
+
/** Build `<cli> auth` with create, manage, configure, login, logout, status, profiles, use, remove. */
|
|
33
|
+
export declare const makeAuthCommand: <I, S extends AuthCommandService>(service: Context.Key<I, S>, descriptor: AtlassianCliDescriptor, options: AuthCommandOptions) => Command.Command<"auth", {}, {}, import("./openBrowser.js").BrowserOpenError | import("../index.ts").OAuthError | import("effect/Terminal").QuitError | (import("../index.ts").FileSystemError | import("../index.ts").HomeDirectoryError | import("effect/PlatformError").PlatformError), import("effect/process/ChildProcessSpawner").ChildProcessSpawner | Exclude<Exclude<I, "effect/cli/GlobalFlag/log-level">, Command.CommandContext<"auth">> | Prompt.Environment>;
|
|
34
|
+
//# sourceMappingURL=authCommand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authCommand.d.ts","sourceRoot":"","sources":["../../src/cli-auth/authCommand.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAY,OAAO,EAAQ,MAAM,EAAE,MAAM,YAAY,CAAA;AAE5D,OAAO,KAAK,KAAK,OAAO,MAAM,gBAAgB,CAAA;AAG9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAOrF,qEAAqE;AACrE,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,gBAAgB,CAAC,KAAK,CAAC,EACrB,WAAW,GACX,OAAO,GACP,QAAQ,GACR,kBAAkB,GAClB,cAAc,GACd,eAAe,GACf,eAAe,CAClB,CAAA;AAED,wEAAwE;AACxE,MAAM,WAAW,kBAAkB;IACjC,oFAAoF;IACpF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,eAAgB,sBAAsB,cAAc,MAAM,KAAG,MAQ1F,CAAA;AAKD,uGAAuG;AACvG,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,CAAC,SAAS,kBAAkB,WACpD,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,cACd,sBAAsB,WACzB,kBAAkB,8cA+J5B,CAAA"}
|