@knpkv/atlassian-common 0.2.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 +11 -0
- package/LICENSE +21 -0
- package/README.md +107 -0
- package/dist/Brand.d.ts +118 -0
- package/dist/Brand.d.ts.map +1 -0
- package/dist/Brand.js +107 -0
- package/dist/Brand.js.map +1 -0
- package/dist/Hash.d.ts +53 -0
- package/dist/Hash.d.ts.map +1 -0
- package/dist/Hash.js +63 -0
- package/dist/Hash.js.map +1 -0
- package/dist/SerializeError.d.ts +48 -0
- package/dist/SerializeError.d.ts.map +1 -0
- package/dist/SerializeError.js +42 -0
- package/dist/SerializeError.js.map +1 -0
- package/dist/ast/BlockNode.d.ts +429 -0
- package/dist/ast/BlockNode.d.ts.map +1 -0
- package/dist/ast/BlockNode.js +279 -0
- package/dist/ast/BlockNode.js.map +1 -0
- package/dist/ast/Document.d.ts +255 -0
- package/dist/ast/Document.d.ts.map +1 -0
- package/dist/ast/Document.js +80 -0
- package/dist/ast/Document.js.map +1 -0
- package/dist/ast/InlineNode.d.ts +483 -0
- package/dist/ast/InlineNode.d.ts.map +1 -0
- package/dist/ast/InlineNode.js +268 -0
- package/dist/ast/InlineNode.js.map +1 -0
- package/dist/ast/MacroNode.d.ts +214 -0
- package/dist/ast/MacroNode.d.ts.map +1 -0
- package/dist/ast/MacroNode.js +110 -0
- package/dist/ast/MacroNode.js.map +1 -0
- package/dist/ast/index.d.ts +10 -0
- package/dist/ast/index.d.ts.map +1 -0
- package/dist/ast/index.js +14 -0
- package/dist/ast/index.js.map +1 -0
- package/dist/auth/OAuthEndpoints.d.ts +95 -0
- package/dist/auth/OAuthEndpoints.d.ts.map +1 -0
- package/dist/auth/OAuthEndpoints.js +112 -0
- package/dist/auth/OAuthEndpoints.js.map +1 -0
- package/dist/auth/OAuthErrors.d.ts +76 -0
- package/dist/auth/OAuthErrors.d.ts.map +1 -0
- package/dist/auth/OAuthErrors.js +85 -0
- package/dist/auth/OAuthErrors.js.map +1 -0
- package/dist/auth/OAuthOperations.d.ts +87 -0
- package/dist/auth/OAuthOperations.d.ts.map +1 -0
- package/dist/auth/OAuthOperations.js +169 -0
- package/dist/auth/OAuthOperations.js.map +1 -0
- package/dist/auth/OAuthResponseSchemas.d.ts +65 -0
- package/dist/auth/OAuthResponseSchemas.d.ts.map +1 -0
- package/dist/auth/OAuthResponseSchemas.js +47 -0
- package/dist/auth/OAuthResponseSchemas.js.map +1 -0
- package/dist/auth/index.d.ts +11 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +16 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/uuid.d.ts +14 -0
- package/dist/auth/uuid.d.ts.map +1 -0
- package/dist/auth/uuid.js +14 -0
- package/dist/auth/uuid.js.map +1 -0
- package/dist/config/ConfigPaths.d.ts +95 -0
- package/dist/config/ConfigPaths.d.ts.map +1 -0
- package/dist/config/ConfigPaths.js +102 -0
- package/dist/config/ConfigPaths.js.map +1 -0
- package/dist/config/OAuthSchemas.d.ts +144 -0
- package/dist/config/OAuthSchemas.d.ts.map +1 -0
- package/dist/config/OAuthSchemas.js +107 -0
- package/dist/config/OAuthSchemas.js.map +1 -0
- package/dist/config/TokenStorage.d.ts +96 -0
- package/dist/config/TokenStorage.d.ts.map +1 -0
- package/dist/config/TokenStorage.js +128 -0
- package/dist/config/TokenStorage.js.map +1 -0
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +12 -0
- package/dist/config/index.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/index.d.ts +7 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +8 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/serializers/MarkdownSerializer.d.ts +63 -0
- package/dist/serializers/MarkdownSerializer.d.ts.map +1 -0
- package/dist/serializers/MarkdownSerializer.js +367 -0
- package/dist/serializers/MarkdownSerializer.js.map +1 -0
- package/dist/serializers/index.d.ts +7 -0
- package/dist/serializers/index.d.ts.map +1 -0
- package/dist/serializers/index.js +7 -0
- package/dist/serializers/index.js.map +1 -0
- package/package.json +86 -0
- package/src/Brand.ts +142 -0
- package/src/Hash.ts +68 -0
- package/src/SerializeError.ts +50 -0
- package/src/ast/BlockNode.ts +386 -0
- package/src/ast/Document.ts +101 -0
- package/src/ast/InlineNode.ts +328 -0
- package/src/ast/MacroNode.ts +172 -0
- package/src/ast/index.ts +87 -0
- package/src/auth/OAuthEndpoints.ts +142 -0
- package/src/auth/OAuthErrors.ts +101 -0
- package/src/auth/OAuthOperations.ts +276 -0
- package/src/auth/OAuthResponseSchemas.ts +70 -0
- package/src/auth/index.ts +47 -0
- package/src/auth/uuid.ts +14 -0
- package/src/config/ConfigPaths.ts +179 -0
- package/src/config/OAuthSchemas.ts +146 -0
- package/src/config/TokenStorage.ts +228 -0
- package/src/config/index.ts +43 -0
- package/src/index.ts +34 -0
- package/src/parsers/index.ts +8 -0
- package/src/serializers/MarkdownSerializer.ts +498 -0
- package/src/serializers/index.ts +7 -0
- package/test/Brand.test.ts +90 -0
- package/test/MarkdownSerializer.test.ts +82 -0
- package/test/OAuthEndpoints.test.ts +109 -0
- package/test/OAuthOperations.test.ts +315 -0
- package/tsconfig.json +11 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +12 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atlassian OAuth2 endpoint URLs, scope constants, authorization URL builder, and PKCE.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Constants + builder**: Endpoint URLs are plain strings; {@link buildAuthUrl}
|
|
7
|
+
* assembles the full authorization URL with audience, scopes, CSRF state, and PKCE.
|
|
8
|
+
* - **Scope sets**: {@link CONFLUENCE_SCOPES} and {@link JIRA_SCOPES} define the
|
|
9
|
+
* minimum permissions for each product's OAuth flow.
|
|
10
|
+
* - **PKCE**: {@link generateCodeVerifier} and {@link computeCodeChallenge} implement
|
|
11
|
+
* RFC 7636 S256 to protect the authorization code grant from interception.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import * as Effect from "effect/Effect";
|
|
16
|
+
/**
|
|
17
|
+
* OAuth2 authorization endpoint.
|
|
18
|
+
*
|
|
19
|
+
* @category Endpoints
|
|
20
|
+
*/
|
|
21
|
+
export declare const AUTH_URL = "https://auth.atlassian.com/authorize";
|
|
22
|
+
/**
|
|
23
|
+
* OAuth2 token endpoint.
|
|
24
|
+
*
|
|
25
|
+
* @category Endpoints
|
|
26
|
+
*/
|
|
27
|
+
export declare const TOKEN_URL = "https://auth.atlassian.com/oauth/token";
|
|
28
|
+
/**
|
|
29
|
+
* OAuth2 token revocation endpoint.
|
|
30
|
+
*
|
|
31
|
+
* @category Endpoints
|
|
32
|
+
*/
|
|
33
|
+
export declare const REVOKE_URL = "https://auth.atlassian.com/oauth/revoke";
|
|
34
|
+
/**
|
|
35
|
+
* Accessible resources endpoint (to get cloud IDs).
|
|
36
|
+
*
|
|
37
|
+
* @category Endpoints
|
|
38
|
+
*/
|
|
39
|
+
export declare const RESOURCES_URL = "https://api.atlassian.com/oauth/token/accessible-resources";
|
|
40
|
+
/**
|
|
41
|
+
* User info endpoint.
|
|
42
|
+
*
|
|
43
|
+
* @category Endpoints
|
|
44
|
+
*/
|
|
45
|
+
export declare const ME_URL = "https://api.atlassian.com/me";
|
|
46
|
+
/**
|
|
47
|
+
* Confluence scopes for OAuth2.
|
|
48
|
+
*
|
|
49
|
+
* @category Scopes
|
|
50
|
+
*/
|
|
51
|
+
export declare const CONFLUENCE_SCOPES: string[];
|
|
52
|
+
/**
|
|
53
|
+
* Jira scopes for OAuth2.
|
|
54
|
+
*
|
|
55
|
+
* @category Scopes
|
|
56
|
+
*/
|
|
57
|
+
export declare const JIRA_SCOPES: string[];
|
|
58
|
+
/**
|
|
59
|
+
* Options for building OAuth2 authorization URL.
|
|
60
|
+
*
|
|
61
|
+
* @category Types
|
|
62
|
+
*/
|
|
63
|
+
export interface BuildAuthUrlOptions {
|
|
64
|
+
/** OAuth2 client ID */
|
|
65
|
+
readonly clientId: string;
|
|
66
|
+
/** State parameter for CSRF protection */
|
|
67
|
+
readonly state: string;
|
|
68
|
+
/** Local callback server port */
|
|
69
|
+
readonly port: number;
|
|
70
|
+
/** OAuth2 scopes */
|
|
71
|
+
readonly scopes: ReadonlyArray<string>;
|
|
72
|
+
/** PKCE code_challenge (S256). Omit to skip PKCE. */
|
|
73
|
+
readonly codeChallenge?: string | undefined;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Build OAuth2 authorization URL.
|
|
77
|
+
*
|
|
78
|
+
* @category Utilities
|
|
79
|
+
*/
|
|
80
|
+
export declare const buildAuthUrl: (options: BuildAuthUrlOptions) => string;
|
|
81
|
+
/**
|
|
82
|
+
* Generate a cryptographically random PKCE code verifier (RFC 7636).
|
|
83
|
+
* Uses Web Crypto API (available in Node 18+, Deno, browsers).
|
|
84
|
+
*
|
|
85
|
+
* @category PKCE
|
|
86
|
+
*/
|
|
87
|
+
export declare const generateCodeVerifier: () => string;
|
|
88
|
+
/**
|
|
89
|
+
* Compute S256 code challenge from a code verifier (RFC 7636).
|
|
90
|
+
* Uses Web Crypto `subtle.digest` — returns Effect since digest is async.
|
|
91
|
+
*
|
|
92
|
+
* @category PKCE
|
|
93
|
+
*/
|
|
94
|
+
export declare const computeCodeChallenge: (verifier: string) => Effect.Effect<string>;
|
|
95
|
+
//# sourceMappingURL=OAuthEndpoints.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthEndpoints.d.ts","sourceRoot":"","sources":["../../src/auth/OAuthEndpoints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC;;;;GAIG;AACH,eAAO,MAAM,QAAQ,yCAAyC,CAAA;AAE9D;;;;GAIG;AACH,eAAO,MAAM,SAAS,2CAA2C,CAAA;AAEjE;;;;GAIG;AACH,eAAO,MAAM,UAAU,4CAA4C,CAAA;AAEnE;;;;GAIG;AACH,eAAO,MAAM,aAAa,+DAA+D,CAAA;AAEzF;;;;GAIG;AACH,eAAO,MAAM,MAAM,iCAAiC,CAAA;AAEpD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,UAM7B,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,WAAW,UAQvB,CAAA;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,uBAAuB;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,0CAA0C;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,iCAAiC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,oBAAoB;IACpB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACtC,qDAAqD;IACrD,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC5C;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,SAAS,mBAAmB,KAAG,MAe3D,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,QAAO,MAIvC,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,UAAU,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAGzE,CAAA"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atlassian OAuth2 endpoint URLs, scope constants, authorization URL builder, and PKCE.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Constants + builder**: Endpoint URLs are plain strings; {@link buildAuthUrl}
|
|
7
|
+
* assembles the full authorization URL with audience, scopes, CSRF state, and PKCE.
|
|
8
|
+
* - **Scope sets**: {@link CONFLUENCE_SCOPES} and {@link JIRA_SCOPES} define the
|
|
9
|
+
* minimum permissions for each product's OAuth flow.
|
|
10
|
+
* - **PKCE**: {@link generateCodeVerifier} and {@link computeCodeChallenge} implement
|
|
11
|
+
* RFC 7636 S256 to protect the authorization code grant from interception.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import * as Effect from "effect/Effect";
|
|
16
|
+
import * as Encoding from "effect/Encoding";
|
|
17
|
+
/**
|
|
18
|
+
* OAuth2 authorization endpoint.
|
|
19
|
+
*
|
|
20
|
+
* @category Endpoints
|
|
21
|
+
*/
|
|
22
|
+
export const AUTH_URL = "https://auth.atlassian.com/authorize";
|
|
23
|
+
/**
|
|
24
|
+
* OAuth2 token endpoint.
|
|
25
|
+
*
|
|
26
|
+
* @category Endpoints
|
|
27
|
+
*/
|
|
28
|
+
export const TOKEN_URL = "https://auth.atlassian.com/oauth/token";
|
|
29
|
+
/**
|
|
30
|
+
* OAuth2 token revocation endpoint.
|
|
31
|
+
*
|
|
32
|
+
* @category Endpoints
|
|
33
|
+
*/
|
|
34
|
+
export const REVOKE_URL = "https://auth.atlassian.com/oauth/revoke";
|
|
35
|
+
/**
|
|
36
|
+
* Accessible resources endpoint (to get cloud IDs).
|
|
37
|
+
*
|
|
38
|
+
* @category Endpoints
|
|
39
|
+
*/
|
|
40
|
+
export const RESOURCES_URL = "https://api.atlassian.com/oauth/token/accessible-resources";
|
|
41
|
+
/**
|
|
42
|
+
* User info endpoint.
|
|
43
|
+
*
|
|
44
|
+
* @category Endpoints
|
|
45
|
+
*/
|
|
46
|
+
export const ME_URL = "https://api.atlassian.com/me";
|
|
47
|
+
/**
|
|
48
|
+
* Confluence scopes for OAuth2.
|
|
49
|
+
*
|
|
50
|
+
* @category Scopes
|
|
51
|
+
*/
|
|
52
|
+
export const CONFLUENCE_SCOPES = [
|
|
53
|
+
"read:page:confluence",
|
|
54
|
+
"write:page:confluence",
|
|
55
|
+
"delete:page:confluence",
|
|
56
|
+
"read:me",
|
|
57
|
+
"offline_access"
|
|
58
|
+
];
|
|
59
|
+
/**
|
|
60
|
+
* Jira scopes for OAuth2.
|
|
61
|
+
*
|
|
62
|
+
* @category Scopes
|
|
63
|
+
*/
|
|
64
|
+
export const JIRA_SCOPES = [
|
|
65
|
+
"read:jira-work",
|
|
66
|
+
"write:jira-work",
|
|
67
|
+
"read:jira-user",
|
|
68
|
+
"manage:jira-project",
|
|
69
|
+
"manage:jira-configuration",
|
|
70
|
+
"read:me",
|
|
71
|
+
"offline_access"
|
|
72
|
+
];
|
|
73
|
+
/**
|
|
74
|
+
* Build OAuth2 authorization URL.
|
|
75
|
+
*
|
|
76
|
+
* @category Utilities
|
|
77
|
+
*/
|
|
78
|
+
export const buildAuthUrl = (options) => {
|
|
79
|
+
const params = new URLSearchParams({
|
|
80
|
+
audience: "api.atlassian.com",
|
|
81
|
+
client_id: options.clientId,
|
|
82
|
+
scope: options.scopes.join(" "),
|
|
83
|
+
redirect_uri: `http://localhost:${options.port}/callback`,
|
|
84
|
+
state: options.state,
|
|
85
|
+
response_type: "code",
|
|
86
|
+
prompt: "consent"
|
|
87
|
+
});
|
|
88
|
+
if (options.codeChallenge) {
|
|
89
|
+
params.set("code_challenge", options.codeChallenge);
|
|
90
|
+
params.set("code_challenge_method", "S256");
|
|
91
|
+
}
|
|
92
|
+
return `${AUTH_URL}?${params.toString()}`;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Generate a cryptographically random PKCE code verifier (RFC 7636).
|
|
96
|
+
* Uses Web Crypto API (available in Node 18+, Deno, browsers).
|
|
97
|
+
*
|
|
98
|
+
* @category PKCE
|
|
99
|
+
*/
|
|
100
|
+
export const generateCodeVerifier = () => {
|
|
101
|
+
const bytes = new Uint8Array(32);
|
|
102
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
103
|
+
return Encoding.encodeBase64Url(bytes);
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Compute S256 code challenge from a code verifier (RFC 7636).
|
|
107
|
+
* Uses Web Crypto `subtle.digest` — returns Effect since digest is async.
|
|
108
|
+
*
|
|
109
|
+
* @category PKCE
|
|
110
|
+
*/
|
|
111
|
+
export const computeCodeChallenge = (verifier) => Effect.promise(() => globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier))).pipe(Effect.map((hash) => Encoding.encodeBase64Url(new Uint8Array(hash))));
|
|
112
|
+
//# sourceMappingURL=OAuthEndpoints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthEndpoints.js","sourceRoot":"","sources":["../../src/auth/OAuthEndpoints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAE3C;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,sCAAsC,CAAA;AAE9D;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,wCAAwC,CAAA;AAEjE;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,yCAAyC,CAAA;AAEnE;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,4DAA4D,CAAA;AAEzF;;;;GAIG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,8BAA8B,CAAA;AAEpD;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,sBAAsB;IACtB,uBAAuB;IACvB,wBAAwB;IACxB,SAAS;IACT,gBAAgB;CACjB,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,gBAAgB;IAChB,iBAAiB;IACjB,gBAAgB;IAChB,qBAAqB;IACrB,2BAA2B;IAC3B,SAAS;IACT,gBAAgB;CACjB,CAAA;AAoBD;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAA4B,EAAU,EAAE;IACnE,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,QAAQ,EAAE,mBAAmB;QAC7B,SAAS,EAAE,OAAO,CAAC,QAAQ;QAC3B,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QAC/B,YAAY,EAAE,oBAAoB,OAAO,CAAC,IAAI,WAAW;QACzD,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,aAAa,EAAE,MAAM;QACrB,MAAM,EAAE,SAAS;KAClB,CAAC,CAAA;IACF,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;QAC1B,MAAM,CAAC,GAAG,CAAC,gBAAgB,EAAE,OAAO,CAAC,aAAa,CAAC,CAAA;QACnD,MAAM,CAAC,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAA;IAC7C,CAAC;IACD,OAAO,GAAG,QAAQ,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAA;AAC3C,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAW,EAAE;IAC/C,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAA;IAChC,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAA;IACxC,OAAO,QAAQ,CAAC,eAAe,CAAC,KAAK,CAAC,CAAA;AACxC,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,QAAgB,EAAyB,EAAE,CAC9E,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CACvG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CACrE,CAAA"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth flow step for error context.
|
|
3
|
+
*
|
|
4
|
+
* @category Types
|
|
5
|
+
*/
|
|
6
|
+
export type OAuthStep = "configure" | "authorize" | "token" | "resources" | "user-info" | "refresh" | "revoke";
|
|
7
|
+
declare const OAuthError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
8
|
+
readonly _tag: "OAuthError";
|
|
9
|
+
} & Readonly<A>;
|
|
10
|
+
/**
|
|
11
|
+
* Error during OAuth2 flow.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { Effect } from "effect"
|
|
16
|
+
* import { OAuthError } from "@knpkv/atlassian-common/auth"
|
|
17
|
+
*
|
|
18
|
+
* Effect.gen(function* () {
|
|
19
|
+
* // ... oauth operation
|
|
20
|
+
* }).pipe(
|
|
21
|
+
* Effect.catchTag("OAuthError", (error) =>
|
|
22
|
+
* Effect.sync(() => console.error(`OAuth error at ${error.step}: ${error.message}`))
|
|
23
|
+
* )
|
|
24
|
+
* )
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @category Errors
|
|
28
|
+
*/
|
|
29
|
+
export declare class OAuthError extends OAuthError_base<{
|
|
30
|
+
readonly step: OAuthStep;
|
|
31
|
+
readonly cause?: unknown;
|
|
32
|
+
}> {
|
|
33
|
+
get message(): string;
|
|
34
|
+
}
|
|
35
|
+
declare const AuthMissingError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
36
|
+
readonly _tag: "AuthMissingError";
|
|
37
|
+
} & Readonly<A>;
|
|
38
|
+
/**
|
|
39
|
+
* Error when authentication is missing.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* import { Effect } from "effect"
|
|
44
|
+
* import { AuthMissingError } from "@knpkv/atlassian-common/auth"
|
|
45
|
+
*
|
|
46
|
+
* Effect.gen(function* () {
|
|
47
|
+
* // ... requires auth
|
|
48
|
+
* }).pipe(
|
|
49
|
+
* Effect.catchTag("AuthMissingError", () =>
|
|
50
|
+
* Effect.sync(() => console.error("Please login first"))
|
|
51
|
+
* )
|
|
52
|
+
* )
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @category Errors
|
|
56
|
+
*/
|
|
57
|
+
export declare class AuthMissingError extends AuthMissingError_base<{
|
|
58
|
+
readonly tool?: string;
|
|
59
|
+
}> {
|
|
60
|
+
get message(): string;
|
|
61
|
+
}
|
|
62
|
+
declare const OAuthNotConfiguredError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
63
|
+
readonly _tag: "OAuthNotConfiguredError";
|
|
64
|
+
} & Readonly<A>;
|
|
65
|
+
/**
|
|
66
|
+
* Error when OAuth is not configured.
|
|
67
|
+
*
|
|
68
|
+
* @category Errors
|
|
69
|
+
*/
|
|
70
|
+
export declare class OAuthNotConfiguredError extends OAuthNotConfiguredError_base<{
|
|
71
|
+
readonly tool?: string;
|
|
72
|
+
}> {
|
|
73
|
+
get message(): string;
|
|
74
|
+
}
|
|
75
|
+
export {};
|
|
76
|
+
//# sourceMappingURL=OAuthErrors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthErrors.d.ts","sourceRoot":"","sources":["../../src/auth/OAuthErrors.ts"],"names":[],"mappings":"AAmBA;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,GAAG,WAAW,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAA;;;;AAE9G;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,UAAW,SAAQ,gBAA+B;IAC7D,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CACzB,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAQ7B;CACF;;;;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,gBAAiB,SAAQ,sBAAqC;IACzE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAG7B;CACF;;;;AAED;;;;GAIG;AACH,qBAAa,uBAAwB,SAAQ,6BAA4C;IACvF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAG7B;CACF"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tagged error types for Atlassian OAuth2 flows.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Step-scoped errors**: {@link OAuthError} carries a `step` field (`"configure" | "authorize" |
|
|
7
|
+
* "token" | "refresh" | "revoke"`) so callers can handle failures per-phase.
|
|
8
|
+
* - **Companion errors**: {@link AuthMissingError} (not logged in) and
|
|
9
|
+
* {@link OAuthNotConfiguredError} (no client credentials) represent pre-flow failures.
|
|
10
|
+
*
|
|
11
|
+
* **Gotchas**
|
|
12
|
+
*
|
|
13
|
+
* - `OAuthError.message` is a computed getter derived from `step` + `cause` — it's not
|
|
14
|
+
* a stored field, so don't destructure it from the constructor.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import * as Data from "effect/Data";
|
|
19
|
+
/**
|
|
20
|
+
* Error during OAuth2 flow.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* import { Effect } from "effect"
|
|
25
|
+
* import { OAuthError } from "@knpkv/atlassian-common/auth"
|
|
26
|
+
*
|
|
27
|
+
* Effect.gen(function* () {
|
|
28
|
+
* // ... oauth operation
|
|
29
|
+
* }).pipe(
|
|
30
|
+
* Effect.catchTag("OAuthError", (error) =>
|
|
31
|
+
* Effect.sync(() => console.error(`OAuth error at ${error.step}: ${error.message}`))
|
|
32
|
+
* )
|
|
33
|
+
* )
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @category Errors
|
|
37
|
+
*/
|
|
38
|
+
export class OAuthError extends Data.TaggedError("OAuthError") {
|
|
39
|
+
get message() {
|
|
40
|
+
if (this.cause instanceof Error) {
|
|
41
|
+
return `OAuth ${this.step} failed: ${this.cause.message}`;
|
|
42
|
+
}
|
|
43
|
+
if (typeof this.cause === "string") {
|
|
44
|
+
return `OAuth ${this.step} failed: ${this.cause}`;
|
|
45
|
+
}
|
|
46
|
+
return `OAuth ${this.step} failed`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Error when authentication is missing.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* import { Effect } from "effect"
|
|
55
|
+
* import { AuthMissingError } from "@knpkv/atlassian-common/auth"
|
|
56
|
+
*
|
|
57
|
+
* Effect.gen(function* () {
|
|
58
|
+
* // ... requires auth
|
|
59
|
+
* }).pipe(
|
|
60
|
+
* Effect.catchTag("AuthMissingError", () =>
|
|
61
|
+
* Effect.sync(() => console.error("Please login first"))
|
|
62
|
+
* )
|
|
63
|
+
* )
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @category Errors
|
|
67
|
+
*/
|
|
68
|
+
export class AuthMissingError extends Data.TaggedError("AuthMissingError") {
|
|
69
|
+
get message() {
|
|
70
|
+
const toolPart = this.tool ? ` for ${this.tool}` : "";
|
|
71
|
+
return `Not logged in${toolPart}. Please run 'auth login' first.`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Error when OAuth is not configured.
|
|
76
|
+
*
|
|
77
|
+
* @category Errors
|
|
78
|
+
*/
|
|
79
|
+
export class OAuthNotConfiguredError extends Data.TaggedError("OAuthNotConfiguredError") {
|
|
80
|
+
get message() {
|
|
81
|
+
const toolPart = this.tool ? `'${this.tool} auth configure'` : "'auth configure'";
|
|
82
|
+
return `OAuth not configured. Run ${toolPart} first.`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=OAuthErrors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthErrors.js","sourceRoot":"","sources":["../../src/auth/OAuthErrors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,IAAI,MAAM,aAAa,CAAA;AASnC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,UAAW,SAAQ,IAAI,CAAC,WAAW,CAAC,YAAY,CAG3D;IACA,IAAa,OAAO;QAClB,IAAI,IAAI,CAAC,KAAK,YAAY,KAAK,EAAE,CAAC;YAChC,OAAO,SAAS,IAAI,CAAC,IAAI,YAAY,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAA;QAC3D,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACnC,OAAO,SAAS,IAAI,CAAC,IAAI,YAAY,IAAI,CAAC,KAAK,EAAE,CAAA;QACnD,CAAC;QACD,OAAO,SAAS,IAAI,CAAC,IAAI,SAAS,CAAA;IACpC,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,gBAAiB,SAAQ,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAEvE;IACA,IAAa,OAAO;QAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QACrD,OAAO,gBAAgB,QAAQ,kCAAkC,CAAA;IACnE,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,uBAAwB,SAAQ,IAAI,CAAC,WAAW,CAAC,yBAAyB,CAErF;IACA,IAAa,OAAO;QAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,kBAAkB,CAAC,CAAC,CAAC,kBAAkB,CAAA;QACjF,OAAO,6BAA6B,QAAQ,SAAS,CAAA;IACvD,CAAC;CACF"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core OAuth2 operations against Atlassian's token and resource endpoints.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Effect-native HTTP**: Every operation takes an `HttpClient` from the Effect
|
|
7
|
+
* context and returns `Effect<A, OAuthError, HttpClient>`. Callers provide the
|
|
8
|
+
* client via `Layer.succeed`.
|
|
9
|
+
* - **Schema-validated responses**: Token and user payloads are decoded through
|
|
10
|
+
* Effect Schema before returning, catching API contract drift at runtime.
|
|
11
|
+
*
|
|
12
|
+
* **Common tasks**
|
|
13
|
+
*
|
|
14
|
+
* - Exchange auth code: {@link exchangeCodeForTokens}
|
|
15
|
+
* - List user sites: {@link getAccessibleResources}
|
|
16
|
+
* - Refresh expired token: {@link refreshToken}
|
|
17
|
+
* - Build storable token: {@link buildOAuthToken}
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
import * as HttpClient from "@effect/platform/HttpClient";
|
|
22
|
+
import * as Effect from "effect/Effect";
|
|
23
|
+
import { type OAuthConfig, type OAuthToken } from "../config/OAuthSchemas.js";
|
|
24
|
+
import { OAuthError } from "./OAuthErrors.js";
|
|
25
|
+
import { type AccessibleResource, type TokenResponse, type UserInfo } from "./OAuthResponseSchemas.js";
|
|
26
|
+
/**
|
|
27
|
+
* Options for exchanging an authorization code for tokens.
|
|
28
|
+
*
|
|
29
|
+
* @category Types
|
|
30
|
+
*/
|
|
31
|
+
export interface ExchangeCodeOptions {
|
|
32
|
+
/** Local callback server port (for redirect_uri) */
|
|
33
|
+
readonly port: number;
|
|
34
|
+
/** PKCE code verifier (if PKCE was used in auth request) */
|
|
35
|
+
readonly codeVerifier?: string | undefined;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Exchange authorization code for tokens.
|
|
39
|
+
*
|
|
40
|
+
* @category Operations
|
|
41
|
+
*/
|
|
42
|
+
export declare const exchangeCodeForTokens: (code: string, config: OAuthConfig, options: ExchangeCodeOptions) => Effect.Effect<TokenResponse, OAuthError, HttpClient.HttpClient>;
|
|
43
|
+
/**
|
|
44
|
+
* Get accessible resources (sites) for the authenticated user.
|
|
45
|
+
*
|
|
46
|
+
* @param accessToken - OAuth access token
|
|
47
|
+
*
|
|
48
|
+
* @category Operations
|
|
49
|
+
*/
|
|
50
|
+
export declare const getAccessibleResources: (accessToken: string) => Effect.Effect<ReadonlyArray<AccessibleResource>, OAuthError, HttpClient.HttpClient>;
|
|
51
|
+
/**
|
|
52
|
+
* Get user info from /me endpoint.
|
|
53
|
+
*
|
|
54
|
+
* @param accessToken - OAuth access token
|
|
55
|
+
*
|
|
56
|
+
* @category Operations
|
|
57
|
+
*/
|
|
58
|
+
export declare const getUserInfo: (accessToken: string) => Effect.Effect<UserInfo, OAuthError, HttpClient.HttpClient>;
|
|
59
|
+
/**
|
|
60
|
+
* Refresh an expired OAuth token.
|
|
61
|
+
*
|
|
62
|
+
* @param token - Current OAuth token (with refresh_token)
|
|
63
|
+
* @param config - OAuth client configuration
|
|
64
|
+
*
|
|
65
|
+
* @category Operations
|
|
66
|
+
*/
|
|
67
|
+
export declare const refreshToken: (token: OAuthToken, config: OAuthConfig) => Effect.Effect<OAuthToken, OAuthError, HttpClient.HttpClient>;
|
|
68
|
+
/**
|
|
69
|
+
* Revoke an OAuth token.
|
|
70
|
+
*
|
|
71
|
+
* @param token - OAuth token to revoke
|
|
72
|
+
* @param config - OAuth client configuration
|
|
73
|
+
*
|
|
74
|
+
* @category Operations
|
|
75
|
+
*/
|
|
76
|
+
export declare const revokeToken: (token: OAuthToken, config: OAuthConfig) => Effect.Effect<void, OAuthError, HttpClient.HttpClient>;
|
|
77
|
+
/**
|
|
78
|
+
* Build OAuthToken from token response and site info.
|
|
79
|
+
*
|
|
80
|
+
* @param tokenResponse - Token response from exchange
|
|
81
|
+
* @param site - Selected accessible resource
|
|
82
|
+
* @param user - User info
|
|
83
|
+
*
|
|
84
|
+
* @category Utilities
|
|
85
|
+
*/
|
|
86
|
+
export declare const buildOAuthToken: (tokenResponse: TokenResponse, site: AccessibleResource, user: UserInfo) => OAuthToken;
|
|
87
|
+
//# sourceMappingURL=OAuthOperations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthOperations.d.ts","sourceRoot":"","sources":["../../src/auth/OAuthOperations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,UAAU,MAAM,6BAA6B,CAAA;AAEzD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAE7E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EACL,KAAK,kBAAkB,EAEvB,KAAK,aAAa,EAElB,KAAK,QAAQ,EAEd,MAAM,2BAA2B,CAAA;AAElC;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,4DAA4D;IAC5D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC3C;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAChC,MAAM,MAAM,EACZ,QAAQ,WAAW,EACnB,SAAS,mBAAmB,KAC3B,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,CA+B9D,CAAA;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,GACjC,aAAa,MAAM,KAClB,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,kBAAkB,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,CAuBlF,CAAA;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GACtB,aAAa,MAAM,KAClB,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,CAuBzD,CAAA;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GACvB,OAAO,UAAU,EACjB,QAAQ,WAAW,KAClB,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,CAyC1D,CAAA;AAEJ;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,GACtB,OAAO,UAAU,EACjB,QAAQ,WAAW,KAClB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,CAyBpD,CAAA;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAC1B,eAAe,aAAa,EAC5B,MAAM,kBAAkB,EACxB,MAAM,QAAQ,KACb,UAYD,CAAA"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core OAuth2 operations against Atlassian's token and resource endpoints.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Effect-native HTTP**: Every operation takes an `HttpClient` from the Effect
|
|
7
|
+
* context and returns `Effect<A, OAuthError, HttpClient>`. Callers provide the
|
|
8
|
+
* client via `Layer.succeed`.
|
|
9
|
+
* - **Schema-validated responses**: Token and user payloads are decoded through
|
|
10
|
+
* Effect Schema before returning, catching API contract drift at runtime.
|
|
11
|
+
*
|
|
12
|
+
* **Common tasks**
|
|
13
|
+
*
|
|
14
|
+
* - Exchange auth code: {@link exchangeCodeForTokens}
|
|
15
|
+
* - List user sites: {@link getAccessibleResources}
|
|
16
|
+
* - Refresh expired token: {@link refreshToken}
|
|
17
|
+
* - Build storable token: {@link buildOAuthToken}
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
import * as HttpClient from "@effect/platform/HttpClient";
|
|
22
|
+
import * as HttpClientRequest from "@effect/platform/HttpClientRequest";
|
|
23
|
+
import * as Effect from "effect/Effect";
|
|
24
|
+
import * as Schema from "effect/Schema";
|
|
25
|
+
import {} from "../config/OAuthSchemas.js";
|
|
26
|
+
import { ME_URL, RESOURCES_URL, REVOKE_URL, TOKEN_URL } from "./OAuthEndpoints.js";
|
|
27
|
+
import { OAuthError } from "./OAuthErrors.js";
|
|
28
|
+
import { AccessibleResourceSchema, TokenResponseSchema, UserInfoSchema } from "./OAuthResponseSchemas.js";
|
|
29
|
+
/**
|
|
30
|
+
* Exchange authorization code for tokens.
|
|
31
|
+
*
|
|
32
|
+
* @category Operations
|
|
33
|
+
*/
|
|
34
|
+
export const exchangeCodeForTokens = (code, config, options) => Effect.gen(function* () {
|
|
35
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
36
|
+
const tokenBody = {
|
|
37
|
+
grant_type: "authorization_code",
|
|
38
|
+
client_id: config.clientId,
|
|
39
|
+
client_secret: config.clientSecret,
|
|
40
|
+
code,
|
|
41
|
+
redirect_uri: `http://localhost:${options.port}/callback`
|
|
42
|
+
};
|
|
43
|
+
if (options.codeVerifier)
|
|
44
|
+
tokenBody.code_verifier = options.codeVerifier;
|
|
45
|
+
const request = yield* HttpClientRequest.post(TOKEN_URL).pipe(HttpClientRequest.setHeader("Content-Type", "application/json"), HttpClientRequest.bodyJson(tokenBody));
|
|
46
|
+
const response = yield* httpClient.execute(request);
|
|
47
|
+
if (response.status >= 400) {
|
|
48
|
+
const text = yield* response.text;
|
|
49
|
+
yield* Effect.logDebug(`Token exchange failed (${response.status}): ${text}`);
|
|
50
|
+
return yield* Effect.fail(new OAuthError({ step: "token", cause: `HTTP ${response.status}` }));
|
|
51
|
+
}
|
|
52
|
+
const body = yield* response.json;
|
|
53
|
+
return yield* Schema.decodeUnknown(TokenResponseSchema)(body);
|
|
54
|
+
}).pipe(Effect.mapError((cause) => new OAuthError({ step: "token", cause })));
|
|
55
|
+
/**
|
|
56
|
+
* Get accessible resources (sites) for the authenticated user.
|
|
57
|
+
*
|
|
58
|
+
* @param accessToken - OAuth access token
|
|
59
|
+
*
|
|
60
|
+
* @category Operations
|
|
61
|
+
*/
|
|
62
|
+
export const getAccessibleResources = (accessToken) => Effect.gen(function* () {
|
|
63
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
64
|
+
const request = HttpClientRequest.get(RESOURCES_URL).pipe(HttpClientRequest.setHeader("Authorization", `Bearer ${accessToken}`), HttpClientRequest.setHeader("Accept", "application/json"));
|
|
65
|
+
const response = yield* httpClient.execute(request);
|
|
66
|
+
if (response.status >= 400) {
|
|
67
|
+
const text = yield* response.text;
|
|
68
|
+
yield* Effect.logDebug(`Accessible resources failed (${response.status}): ${text}`);
|
|
69
|
+
return yield* Effect.fail(new OAuthError({ step: "resources", cause: `HTTP ${response.status}` }));
|
|
70
|
+
}
|
|
71
|
+
const body = yield* response.json;
|
|
72
|
+
return yield* Schema.decodeUnknown(Schema.Array(AccessibleResourceSchema))(body);
|
|
73
|
+
}).pipe(Effect.mapError((cause) => new OAuthError({ step: "resources", cause })));
|
|
74
|
+
/**
|
|
75
|
+
* Get user info from /me endpoint.
|
|
76
|
+
*
|
|
77
|
+
* @param accessToken - OAuth access token
|
|
78
|
+
*
|
|
79
|
+
* @category Operations
|
|
80
|
+
*/
|
|
81
|
+
export const getUserInfo = (accessToken) => Effect.gen(function* () {
|
|
82
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
83
|
+
const request = HttpClientRequest.get(ME_URL).pipe(HttpClientRequest.setHeader("Authorization", `Bearer ${accessToken}`), HttpClientRequest.setHeader("Accept", "application/json"));
|
|
84
|
+
const response = yield* httpClient.execute(request);
|
|
85
|
+
if (response.status >= 400) {
|
|
86
|
+
const text = yield* response.text;
|
|
87
|
+
yield* Effect.logDebug(`User info failed (${response.status}): ${text}`);
|
|
88
|
+
return yield* Effect.fail(new OAuthError({ step: "user-info", cause: `HTTP ${response.status}` }));
|
|
89
|
+
}
|
|
90
|
+
const body = yield* response.json;
|
|
91
|
+
return yield* Schema.decodeUnknown(UserInfoSchema)(body);
|
|
92
|
+
}).pipe(Effect.mapError((cause) => new OAuthError({ step: "user-info", cause })));
|
|
93
|
+
/**
|
|
94
|
+
* Refresh an expired OAuth token.
|
|
95
|
+
*
|
|
96
|
+
* @param token - Current OAuth token (with refresh_token)
|
|
97
|
+
* @param config - OAuth client configuration
|
|
98
|
+
*
|
|
99
|
+
* @category Operations
|
|
100
|
+
*/
|
|
101
|
+
export const refreshToken = (token, config) => Effect.gen(function* () {
|
|
102
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
103
|
+
const request = yield* HttpClientRequest.post(TOKEN_URL).pipe(HttpClientRequest.setHeader("Content-Type", "application/json"), HttpClientRequest.bodyJson({
|
|
104
|
+
grant_type: "refresh_token",
|
|
105
|
+
client_id: config.clientId,
|
|
106
|
+
client_secret: config.clientSecret,
|
|
107
|
+
refresh_token: token.refresh_token
|
|
108
|
+
}), Effect.mapError((cause) => new OAuthError({ step: "refresh", cause })));
|
|
109
|
+
const response = yield* httpClient.execute(request).pipe(Effect.mapError((cause) => new OAuthError({ step: "refresh", cause })));
|
|
110
|
+
if (response.status >= 400) {
|
|
111
|
+
const text = yield* response.text.pipe(Effect.mapError((cause) => new OAuthError({ step: "refresh", cause })));
|
|
112
|
+
return yield* Effect.fail(new OAuthError({ step: "refresh", cause: `HTTP ${response.status}: ${text}` }));
|
|
113
|
+
}
|
|
114
|
+
const body = yield* response.json.pipe(Effect.mapError((cause) => new OAuthError({ step: "refresh", cause })));
|
|
115
|
+
const tokenResponse = yield* Schema.decodeUnknown(TokenResponseSchema)(body).pipe(Effect.mapError((cause) => new OAuthError({ step: "refresh", cause })));
|
|
116
|
+
return {
|
|
117
|
+
...token,
|
|
118
|
+
access_token: tokenResponse.access_token,
|
|
119
|
+
refresh_token: tokenResponse.refresh_token,
|
|
120
|
+
expires_at: Date.now() + tokenResponse.expires_in * 1000,
|
|
121
|
+
scope: tokenResponse.scope
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
/**
|
|
125
|
+
* Revoke an OAuth token.
|
|
126
|
+
*
|
|
127
|
+
* @param token - OAuth token to revoke
|
|
128
|
+
* @param config - OAuth client configuration
|
|
129
|
+
*
|
|
130
|
+
* @category Operations
|
|
131
|
+
*/
|
|
132
|
+
export const revokeToken = (token, config) => Effect.gen(function* () {
|
|
133
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
134
|
+
const request = yield* HttpClientRequest.post(REVOKE_URL).pipe(HttpClientRequest.setHeader("Content-Type", "application/json"), HttpClientRequest.bodyJson({
|
|
135
|
+
client_id: config.clientId,
|
|
136
|
+
client_secret: config.clientSecret,
|
|
137
|
+
token: token.refresh_token
|
|
138
|
+
}), Effect.mapError((cause) => new OAuthError({ step: "revoke", cause })));
|
|
139
|
+
const response = yield* httpClient.execute(request).pipe(Effect.mapError((cause) => new OAuthError({ step: "revoke", cause })));
|
|
140
|
+
if (response.status >= 400) {
|
|
141
|
+
return yield* Effect.fail(new OAuthError({
|
|
142
|
+
step: "revoke",
|
|
143
|
+
cause: `Token revocation failed with status ${response.status}`
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
/**
|
|
148
|
+
* Build OAuthToken from token response and site info.
|
|
149
|
+
*
|
|
150
|
+
* @param tokenResponse - Token response from exchange
|
|
151
|
+
* @param site - Selected accessible resource
|
|
152
|
+
* @param user - User info
|
|
153
|
+
*
|
|
154
|
+
* @category Utilities
|
|
155
|
+
*/
|
|
156
|
+
export const buildOAuthToken = (tokenResponse, site, user) => ({
|
|
157
|
+
access_token: tokenResponse.access_token,
|
|
158
|
+
refresh_token: tokenResponse.refresh_token,
|
|
159
|
+
expires_at: Date.now() + tokenResponse.expires_in * 1000,
|
|
160
|
+
scope: tokenResponse.scope,
|
|
161
|
+
cloud_id: site.id,
|
|
162
|
+
site_url: site.url,
|
|
163
|
+
user: {
|
|
164
|
+
account_id: user.account_id,
|
|
165
|
+
name: user.name,
|
|
166
|
+
email: user.email
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
//# sourceMappingURL=OAuthOperations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthOperations.js","sourceRoot":"","sources":["../../src/auth/OAuthOperations.ts"],"names":[],"mappings":"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"}
|