oauth2-cli 0.2.3 → 0.4.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 CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
4
4
 
5
+ ## [0.4.0](https://github.com/battis/oauth2-cli/compare/oauth2-cli/0.3.0...oauth2-cli/0.4.0) (2026-01-04)
6
+
7
+
8
+ ### ⚠ BREAKING CHANGES
9
+
10
+ * remove deprecated TokenManager
11
+
12
+ ### Features
13
+
14
+ * provide fetch() and fetchJSON() methods to translate to openid-client requests ([3fe454f](https://github.com/battis/oauth2-cli/commit/3fe454f28497d704041ea4e599a4ad5b2b08b469))
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * remove deprecated TokenManager ([2dde67e](https://github.com/battis/oauth2-cli/commit/2dde67edd70b151bd1bab6de3845839a06957e65))
20
+
21
+ ## [0.3.0](https://github.com/battis/oauth2-cli/compare/oauth2-cli/0.2.3...oauth2-cli/0.3.0) (2025-12-24)
22
+
23
+
24
+ ### ⚠ BREAKING CHANGES
25
+
26
+ * resolve 1Password secret references successfully
27
+
28
+ ### Bug Fixes
29
+
30
+ * resolve 1Password secret references successfully ([c4446e1](https://github.com/battis/oauth2-cli/commit/c4446e197a66271dac3ea8d58ff44725cc6be1db))
31
+
5
32
  ## [0.2.3](https://github.com/battis/oauth2-cli/compare/oauth2-cli/0.2.2...oauth2-cli/0.2.3) (2025-12-23)
6
33
 
7
34
 
package/dist/Client.d.ts CHANGED
@@ -1,24 +1,57 @@
1
1
  import * as Configuration from '@battis/oauth2-configure';
2
+ import { JSONValue } from '@battis/typescript-tricks';
2
3
  import * as OpenIDClient from 'openid-client';
3
4
  import { Token } from './Token.js';
4
5
  import { TokenStorage } from './TokenStorage.js';
5
- export type Options = Configuration.Options & {
6
+ export type Credentials = Configuration.Options & {
6
7
  scope?: string;
7
8
  headers?: Record<string, string>;
8
9
  parameters?: Record<string, string>;
9
10
  store?: TokenStorage | string;
10
11
  };
12
+ /** Wrap an OpenID configuration in an object-oriented API client. */
11
13
  export declare class Client {
12
- private options;
14
+ private credentials;
13
15
  private tokenMutex;
14
16
  private config?;
15
17
  private token?;
16
18
  private store?;
17
- constructor(options: Options);
19
+ constructor(credentials: Credentials);
20
+ /** Acquire a valid, unexpired API access token. */
18
21
  getToken(): Promise<Token | undefined>;
22
+ /** Refresh Token Grant */
19
23
  private refreshToken;
24
+ /** Acquire a valid OpenID configuration. */
20
25
  private getConfiguration;
26
+ /** Authorization Code Grant */
21
27
  private authorize;
22
- request(url: URL | string, method?: string, body?: OpenIDClient.FetchBody, headers?: Record<string, string>, options?: OpenIDClient.DPoPOptions): Promise<Response>;
23
- requestJSON(url: URL | string, method?: string, body?: OpenIDClient.FetchBody, headers?: Record<string, string>, options?: OpenIDClient.DPoPOptions): Promise<unknown>;
28
+ /**
29
+ * Make an authorized request to the API, acquiring an unexpired acess token
30
+ * if necessary.
31
+ */
32
+ request(url: URL | string, method?: string, body?: OpenIDClient.FetchBody, headers?: Headers, dPoPOptions?: OpenIDClient.DPoPOptions): Promise<Response>;
33
+ /**
34
+ * Make an authorized request to the API, acquiring an unexpired acess token
35
+ * if necessary.
36
+ *
37
+ * This converts the Fetch API request into an OpenID request. To directly
38
+ * make this request, use {@link request()}
39
+ */
40
+ fetch(endpoint: string | URL | Request, init?: RequestInit): Promise<Response>;
41
+ /**
42
+ * Make an authorized request to the API, acquiring an unexpired acess token
43
+ * if necessary and parse the JSON respomse.
44
+ *
45
+ * @param validator Optional validator function test that the JSON response is
46
+ * the expected type.
47
+ */
48
+ requestJSON<T extends JSONValue = JSONValue>(url: URL | string, method?: string, body?: OpenIDClient.FetchBody, headers?: Headers, dPoPOptions?: OpenIDClient.DPoPOptions): Promise<T>;
49
+ /**
50
+ * Make an authorized request to the API, acquiring an unexpired acess token
51
+ * if necessary and parse the JSON respomse.
52
+ *
53
+ * This converts the Fetch API request into an OpenID request. To directly
54
+ * make this request, use {@link requestJSON()}
55
+ */
56
+ fetchJSON<T extends JSONValue = JSONValue>(endpoint: string | URL | Request, init?: RequestInit): Promise<T>;
24
57
  }
package/dist/Client.js CHANGED
@@ -4,23 +4,25 @@ import * as OpenIDClient from 'openid-client';
4
4
  import { FileStorage } from './FileStorage.js';
5
5
  import * as Localhost from './Localhost.js';
6
6
  import { Token } from './Token.js';
7
+ /** Wrap an OpenID configuration in an object-oriented API client. */
7
8
  export class Client {
8
- options;
9
+ credentials;
9
10
  tokenMutex = new Mutex();
10
11
  config;
11
12
  token;
12
13
  store;
13
- constructor(options) {
14
- this.options = options;
15
- if (this.options.store) {
16
- if (typeof this.options.store === 'string') {
17
- this.store = new FileStorage(this.options.store);
14
+ constructor(credentials) {
15
+ this.credentials = credentials;
16
+ if (this.credentials.store) {
17
+ if (typeof this.credentials.store === 'string') {
18
+ this.store = new FileStorage(this.credentials.store);
18
19
  }
19
20
  else {
20
- this.store = this.options.store;
21
+ this.store = this.credentials.store;
21
22
  }
22
23
  }
23
24
  }
25
+ /** Acquire a valid, unexpired API access token. */
24
26
  async getToken() {
25
27
  return await this.tokenMutex.runExclusive((async () => {
26
28
  if (!this.token) {
@@ -35,63 +37,104 @@ export class Client {
35
37
  return this.token;
36
38
  }).bind(this));
37
39
  }
40
+ /** Refresh Token Grant */
38
41
  async refreshToken(token) {
39
42
  if (token.refresh_token) {
40
- const { headers, parameters } = this.options;
43
+ const { headers, parameters } = this.credentials;
41
44
  let freshTokens;
42
- if ((freshTokens = Token.fromResponse(await OpenIDClient.refreshTokenGrant(await Configuration.acquire(this.options), token.refresh_token, parameters,
45
+ if ((freshTokens = Token.fromResponse(await OpenIDClient.refreshTokenGrant(await Configuration.acquire(this.credentials), token.refresh_token, parameters,
43
46
  // @ts-expect-error 2322 undocumented arg pass-through to oauth4webapi
44
47
  { headers }), token.refresh_token))) {
45
- return this.store?.save(freshTokens) || freshTokens;
48
+ if (this.store) {
49
+ await this.store.save(freshTokens);
50
+ }
51
+ return freshTokens;
46
52
  }
47
53
  }
48
54
  return this.authorize();
49
55
  }
56
+ /** Acquire a valid OpenID configuration. */
50
57
  async getConfiguration() {
51
58
  if (!this.config) {
52
- this.config = await Configuration.acquire(this.options);
59
+ this.config = await Configuration.acquire(this.credentials);
53
60
  }
54
61
  return this.config;
55
62
  }
63
+ /** Authorization Code Grant */
56
64
  async authorize() {
57
- const { scope, redirect_uri, parameters: additionalParameters } = this.options;
58
- return new Promise(async (resolve, reject) => {
65
+ const { scope, redirect_uri, parameters: additionalParameters } = this.credentials;
66
+ return new Promise((resolve, reject) => {
59
67
  const code_verifier = OpenIDClient.randomPKCECodeVerifier();
60
- const code_challenge = await OpenIDClient.calculatePKCECodeChallenge(code_verifier);
61
- let state = undefined;
62
- const parameters = {
63
- ...additionalParameters,
64
- redirect_uri,
65
- code_challenge,
66
- code_challenge_method: 'S256' // TODO make code challenge method configurable?
67
- };
68
- if (scope) {
69
- parameters.scope = scope;
70
- }
71
- if (!(await this.getConfiguration()).serverMetadata().supportsPKCE()) {
72
- state = OpenIDClient.randomState();
73
- parameters.state = state;
74
- }
75
- await Localhost.redirectServer({
76
- ...this.options,
77
- authorization_url: OpenIDClient.buildAuthorizationUrl(await this.getConfiguration(), parameters).href,
78
- code_verifier,
79
- state,
80
- resolve: (async (response) => {
81
- this.token = Token.fromResponse(response);
82
- if (this.token && this.store) {
83
- await this.store.save(this.token);
84
- }
85
- resolve(this.token);
86
- }).bind(this),
87
- reject
68
+ OpenIDClient.calculatePKCECodeChallenge(code_verifier).then(async (code_challenge) => {
69
+ let state = undefined;
70
+ const parameters = {
71
+ ...additionalParameters,
72
+ redirect_uri,
73
+ code_challenge,
74
+ code_challenge_method: 'S256' // TODO make code challenge method configurable?
75
+ };
76
+ if (scope) {
77
+ parameters.scope = scope;
78
+ }
79
+ if (!(await this.getConfiguration()).serverMetadata().supportsPKCE()) {
80
+ state = OpenIDClient.randomState();
81
+ parameters.state = state;
82
+ }
83
+ await Localhost.redirectServer({
84
+ ...this.credentials,
85
+ authorization_url: OpenIDClient.buildAuthorizationUrl(await this.getConfiguration(), parameters).href,
86
+ code_verifier,
87
+ state,
88
+ resolve: (async (response) => {
89
+ this.token = Token.fromResponse(response);
90
+ if (this.token && this.store) {
91
+ await this.store.save(this.token);
92
+ }
93
+ resolve(this.token);
94
+ }).bind(this),
95
+ reject
96
+ });
88
97
  });
89
98
  });
90
99
  }
91
- async request(url, method = 'GET', body, headers, options) {
92
- return await OpenIDClient.fetchProtectedResource(await this.getConfiguration(), (await this.getToken()).access_token, new URL(url), method, body, new Headers({ ...this.options.headers, ...headers }), options);
100
+ /**
101
+ * Make an authorized request to the API, acquiring an unexpired acess token
102
+ * if necessary.
103
+ */
104
+ async request(url, method = 'GET', body, headers, dPoPOptions) {
105
+ for (const header in this.credentials.headers) {
106
+ headers?.append(header, this.credentials.headers[header]);
107
+ }
108
+ return await OpenIDClient.fetchProtectedResource(await this.getConfiguration(), (await this.getToken()).access_token, new URL(url), method, body, headers, dPoPOptions);
109
+ }
110
+ /**
111
+ * Make an authorized request to the API, acquiring an unexpired acess token
112
+ * if necessary.
113
+ *
114
+ * This converts the Fetch API request into an OpenID request. To directly
115
+ * make this request, use {@link request()}
116
+ */
117
+ async fetch(endpoint, init) {
118
+ return this.request(endpoint.toString(), init?.method, init?.body?.toString(), new Headers(init?.headers));
119
+ }
120
+ /**
121
+ * Make an authorized request to the API, acquiring an unexpired acess token
122
+ * if necessary and parse the JSON respomse.
123
+ *
124
+ * @param validator Optional validator function test that the JSON response is
125
+ * the expected type.
126
+ */
127
+ async requestJSON(url, method = 'GET', body, headers, dPoPOptions) {
128
+ return (await (await this.request(url, method, body, headers, dPoPOptions)).json());
93
129
  }
94
- async requestJSON(url, method = 'GET', body, headers, options) {
95
- return await (await this.request(url, method, body, headers, options)).json();
130
+ /**
131
+ * Make an authorized request to the API, acquiring an unexpired acess token
132
+ * if necessary and parse the JSON respomse.
133
+ *
134
+ * This converts the Fetch API request into an OpenID request. To directly
135
+ * make this request, use {@link requestJSON()}
136
+ */
137
+ async fetchJSON(endpoint, init) {
138
+ return (await (await this.fetch(endpoint, init)).json());
96
139
  }
97
140
  }
@@ -5,5 +5,5 @@ export declare class FileStorage implements TokenStorage {
5
5
  private readonly filePath;
6
6
  constructor(filePath: string);
7
7
  load(): Promise<Token | undefined>;
8
- save(tokens: Token): Promise<Token>;
8
+ save(tokens: Token): Promise<void>;
9
9
  }
@@ -24,6 +24,5 @@ export class FileStorage {
24
24
  }
25
25
  fs.writeFileSync(this.filePath, JSON.stringify(tokens));
26
26
  }).bind(this));
27
- return tokens;
28
27
  }
29
28
  }
@@ -1,5 +1,5 @@
1
1
  import { Token } from './Token.js';
2
2
  export interface TokenStorage {
3
3
  load(): Promise<Token | undefined>;
4
- save(tokens: Token): Promise<Token>;
4
+ save(tokens: Token): Promise<void>;
5
5
  }
package/dist/index.d.ts CHANGED
@@ -1,8 +1,4 @@
1
- import { Client } from './Client.js';
2
- export { Client, Options as Credentials } from './Client.js';
3
- export { FileStorage } from './FileStorage.js';
4
- export { Token } from './Token.js';
5
- export { TokenStorage } from './TokenStorage.js';
6
- export {
7
- /** @deprecated use Client */
8
- Client as TokenManager };
1
+ export * from './Client.js';
2
+ export * from './FileStorage.js';
3
+ export * from './Token.js';
4
+ export * from './TokenStorage.js';
package/dist/index.js CHANGED
@@ -1,7 +1,4 @@
1
- import { Client } from './Client.js';
2
- export { Client } from './Client.js';
3
- export { FileStorage } from './FileStorage.js';
4
- export { Token } from './Token.js';
5
- export {
6
- /** @deprecated use Client */
7
- Client as TokenManager };
1
+ export * from './Client.js';
2
+ export * from './FileStorage.js';
3
+ export * from './Token.js';
4
+ export * from './TokenStorage.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oauth2-cli",
3
- "version": "0.2.3",
3
+ "version": "0.4.0",
4
4
  "description": "Acquire API access tokens via OAuth 2.0 within CLI tools",
5
5
  "homepage": "https://github.com/battis/oauth2-cli/tree/main/packages/oauth2-cli#readme",
6
6
  "repository": {
@@ -20,9 +20,10 @@
20
20
  "express": "^5.2.1",
21
21
  "open": "^11.0.0",
22
22
  "openid-client": "^6.8.1",
23
- "@battis/oauth2-configure": "0.1.3"
23
+ "@battis/oauth2-configure": "0.1.4"
24
24
  },
25
25
  "devDependencies": {
26
+ "@battis/typescript-tricks": "^0.7.6",
26
27
  "@tsconfig/node20": "^20.1.8",
27
28
  "@types/ejs": "^3.1.5",
28
29
  "@types/express": "^5.0.6",