oauth2-cli 0.6.0 → 0.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 CHANGED
@@ -2,6 +2,17 @@
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.7.0](https://github.com/battis/oauth2-cli/compare/oauth2-cli/0.6.0...oauth2-cli/0.7.0) (2026-02-16)
6
+
7
+
8
+ ### ⚠ BREAKING CHANGES
9
+
10
+ * make more properties of Client accessible to subclasses
11
+
12
+ ### Features
13
+
14
+ * make more properties of Client accessible to subclasses ([74ef874](https://github.com/battis/oauth2-cli/commit/74ef874804323a9c3c496ddf7b0e24bac9e671e1))
15
+
5
16
  ## [0.6.0](https://github.com/battis/oauth2-cli/compare/oauth2-cli/0.5.1...oauth2-cli/0.6.0) (2026-02-15)
6
17
 
7
18
 
package/dist/Client.d.ts CHANGED
@@ -2,6 +2,7 @@ import { PathString } from '@battis/descriptive-types';
2
2
  import { Request } from 'express';
3
3
  import { EventEmitter } from 'node:events';
4
4
  import * as OpenIDClient from 'openid-client';
5
+ import * as requestish from 'requestish';
5
6
  import * as Credentials from './Credentials.js';
6
7
  import * as Req from './Request/index.js';
7
8
  import { Session, SessionOptions } from './Session.js';
@@ -14,6 +15,12 @@ export declare const DEFAULT_REDIRECT_URI = "http://localhost:3000/oauth2-cli/re
14
15
  export type ClientOptions = {
15
16
  /** Credentials for server access */
16
17
  credentials: Credentials.Combined;
18
+ /** Optional request components to inject */
19
+ inject?: {
20
+ search?: requestish.URLSearchParams.ish;
21
+ headers?: requestish.Headers.ish;
22
+ body?: requestish.Body.ish;
23
+ };
17
24
  /**
18
25
  * Optional absolute path to EJS view templates directory, see
19
26
  * [WebServer.setViews()](./Webserver.ts)
@@ -21,21 +28,6 @@ export type ClientOptions = {
21
28
  views?: PathString;
22
29
  /** Optional {@link TokenStorage} implementation to manage tokens */
23
30
  storage?: Token.TokenStorage;
24
- /**
25
- * Optional search query parameters to include in all server requests (see
26
- * {@link RequestAddons.search})
27
- */
28
- search?: Req.URLSearchParams.ish;
29
- /**
30
- * Optional headers to include in all server requests (see
31
- * {@link RequestAddons.headers})
32
- */
33
- headers?: Req.Headers.ish;
34
- /**
35
- * Optional body parameters to include in applicable server requests (see
36
- * {@link RequestAddons.body})
37
- */
38
- body?: Req.Body.ish;
39
31
  };
40
32
  type RefreshOptions = {
41
33
  /**
@@ -71,17 +63,15 @@ type GetTokenOptions = {
71
63
  */
72
64
  export declare class Client extends EventEmitter {
73
65
  static readonly TokenEvent = "token";
74
- private credentials;
75
- private config?;
76
- private views?;
66
+ protected credentials: Credentials.Combined;
67
+ protected config?: OpenIDClient.Configuration;
68
+ protected views?: PathString;
69
+ protected inject?: Req.Injection;
77
70
  private token?;
78
71
  private tokenLock;
79
- private search?;
80
- private headers?;
81
- private body?;
82
72
  private storage?;
83
- constructor({ credentials, views, search, headers, body, storage }: ClientOptions);
84
- get redirect_uri(): Req.URL.ish;
73
+ constructor({ credentials, views, inject, storage }: ClientOptions);
74
+ get redirect_uri(): requestish.URL.ish;
85
75
  /**
86
76
  * @throws IndeterminateConfiguration if provided credentials combined with
87
77
  * OpenID discovery fail to generate a complete configuration
@@ -110,14 +100,14 @@ export declare class Client extends EventEmitter {
110
100
  * @param headers Optional
111
101
  * @param dPoPOptions Optional
112
102
  */
113
- request(url: Req.URL.ish, method?: string, body?: OpenIDClient.FetchBody, headers?: Req.Headers.ish, dPoPOptions?: OpenIDClient.DPoPOptions): Promise<Response>;
103
+ request(url: requestish.URL.ish, method?: string, body?: OpenIDClient.FetchBody, headers?: requestish.Headers.ish, dPoPOptions?: OpenIDClient.DPoPOptions): Promise<Response>;
114
104
  private toJSON;
115
105
  /**
116
106
  * Returns the result of {@link request} as a parsed JSON object, optionally
117
107
  * typed as `T`
118
108
  */
119
- requestJSON<T extends OpenIDClient.JsonValue = OpenIDClient.JsonValue>(url: Req.URL.ish, method?: string, body?: OpenIDClient.FetchBody, headers?: Req.Headers.ish, dPoPOptions?: OpenIDClient.DPoPOptions): Promise<T>;
120
- fetch(input: Req.URL.ish, init?: RequestInit, dPoPOptions?: OpenIDClient.DPoPOptions): Promise<Response>;
121
- fetchJSON<T extends OpenIDClient.JsonValue = OpenIDClient.JsonValue>(input: Req.URL.ish, init?: RequestInit, dPoPOptions?: OpenIDClient.DPoPOptions): Promise<T>;
109
+ requestJSON<T extends OpenIDClient.JsonValue = OpenIDClient.JsonValue>(url: requestish.URL.ish, method?: string, body?: OpenIDClient.FetchBody, headers?: requestish.Headers.ish, dPoPOptions?: OpenIDClient.DPoPOptions): Promise<T>;
110
+ fetch(input: requestish.URL.ish, init?: RequestInit, dPoPOptions?: OpenIDClient.DPoPOptions): Promise<Response>;
111
+ fetchJSON<T extends OpenIDClient.JsonValue = OpenIDClient.JsonValue>(input: requestish.URL.ish, init?: RequestInit, dPoPOptions?: OpenIDClient.DPoPOptions): Promise<T>;
122
112
  }
123
113
  export {};
package/dist/Client.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Mutex } from 'async-mutex';
2
2
  import { EventEmitter } from 'node:events';
3
3
  import * as OpenIDClient from 'openid-client';
4
+ import * as requestish from 'requestish';
4
5
  import * as Errors from './Errors/index.js';
5
6
  import * as Req from './Request/index.js';
6
7
  import { Session } from './Session.js';
@@ -21,19 +22,15 @@ export class Client extends EventEmitter {
21
22
  credentials;
22
23
  config;
23
24
  views;
25
+ inject;
24
26
  token;
25
27
  tokenLock = new Mutex();
26
- search;
27
- headers;
28
- body;
29
28
  storage;
30
- constructor({ credentials, views, search, headers, body, storage }) {
29
+ constructor({ credentials, views, inject, storage }) {
31
30
  super();
32
31
  this.credentials = credentials;
33
32
  this.views = views;
34
- this.search = search;
35
- this.headers = headers;
36
- this.body = body;
33
+ this.inject = inject;
37
34
  this.storage = storage;
38
35
  }
39
36
  get redirect_uri() {
@@ -45,13 +42,13 @@ export class Client extends EventEmitter {
45
42
  */
46
43
  async getConfiguration() {
47
44
  if (!this.config && this.credentials.issuer) {
48
- this.config = await OpenIDClient.discovery(Req.URL.from(this.credentials.issuer), this.credentials.client_id, { client_secret: this.credentials.client_secret });
45
+ this.config = await OpenIDClient.discovery(requestish.URL.from(this.credentials.issuer), this.credentials.client_id, { client_secret: this.credentials.client_secret });
49
46
  }
50
47
  if (!this.config && this.credentials?.authorization_endpoint) {
51
48
  this.config = new OpenIDClient.Configuration({
52
- issuer: `https://${Req.URL.from(this.credentials.authorization_endpoint).hostname}`,
53
- authorization_endpoint: Req.URL.toString(this.credentials.authorization_endpoint),
54
- token_endpoint: Req.URL.toString(this.credentials.token_endpoint ||
49
+ issuer: `https://${requestish.URL.from(this.credentials.authorization_endpoint).hostname}`,
50
+ authorization_endpoint: requestish.URL.toString(this.credentials.authorization_endpoint),
51
+ token_endpoint: requestish.URL.toString(this.credentials.token_endpoint ||
55
52
  this.credentials.authorization_endpoint)
56
53
  }, this.credentials.client_id, { client_secret: this.credentials.client_secret });
57
54
  }
@@ -61,9 +58,8 @@ export class Client extends EventEmitter {
61
58
  return this.config;
62
59
  }
63
60
  async getParameters(session) {
64
- const params = Req.URLSearchParams.merge(this.search, session.inject?.search) ||
65
- new URLSearchParams();
66
- params.set('redirect_uri', Req.URL.toString(this.credentials.redirect_uri));
61
+ const params = requestish.URLSearchParams.merge(this.inject?.search, session.inject?.search) || new URLSearchParams();
62
+ params.set('redirect_uri', requestish.URL.toString(this.credentials.redirect_uri));
67
63
  params.set('code_challenge', await OpenIDClient.calculatePKCECodeChallenge(session.code_verifier));
68
64
  params.set('code_challenge_method', 'S256');
69
65
  params.set('state', session.state);
@@ -92,7 +88,9 @@ export class Client extends EventEmitter {
92
88
  const response = await OpenIDClient.authorizationCodeGrant(await this.getConfiguration(), new URL(req.url, this.redirect_uri), {
93
89
  pkceCodeVerifier: session.code_verifier,
94
90
  expectedState: session.state
95
- }, this.search ? Req.URLSearchParams.from(this.search) : undefined);
91
+ }, this.inject?.search
92
+ ? requestish.URLSearchParams.from(this.inject.search)
93
+ : undefined);
96
94
  await session.resolve(response);
97
95
  }
98
96
  catch (error) {
@@ -106,7 +104,9 @@ export class Client extends EventEmitter {
106
104
  if (!refresh_token || refresh_token === '') {
107
105
  return undefined;
108
106
  }
109
- const token = await OpenIDClient.refreshTokenGrant(await this.getConfiguration(), refresh_token, this.search ? Req.URLSearchParams.from(this.search) : undefined, {
107
+ const token = await OpenIDClient.refreshTokenGrant(await this.getConfiguration(), refresh_token, this.inject?.search
108
+ ? requestish.URLSearchParams.from(this.inject.search)
109
+ : undefined, {
110
110
  // @ts-expect-error 2322 undocumented arg pass-through to oauth4webapi
111
111
  headers: Req.Headers.merge(this.headers, request?.headers)
112
112
  });
@@ -152,7 +152,7 @@ export class Client extends EventEmitter {
152
152
  */
153
153
  async request(url, method = 'GET', body, headers = {}, dPoPOptions) {
154
154
  try {
155
- url = Req.URL.from(url);
155
+ url = requestish.URL.from(url);
156
156
  }
157
157
  catch (error) {
158
158
  if (this.credentials.issuer) {
@@ -162,7 +162,7 @@ export class Client extends EventEmitter {
162
162
  throw error;
163
163
  }
164
164
  }
165
- const request = async () => await OpenIDClient.fetchProtectedResource(await this.getConfiguration(), (await this.getToken()).access_token, Req.URL.from(Req.URLSearchParams.appendTo(url, this.search || {})), method, body, Req.Headers.merge(this.headers, headers), dPoPOptions);
165
+ const request = async () => await OpenIDClient.fetchProtectedResource(await this.getConfiguration(), (await this.getToken()).access_token, requestish.URL.from(requestish.URLSearchParams.appendTo(url, this.inject?.search || {})), method, body, requestish.Headers.merge(this.inject?.headers, headers), dPoPOptions);
166
166
  try {
167
167
  return await request();
168
168
  }
@@ -195,7 +195,7 @@ export class Client extends EventEmitter {
195
195
  return await this.toJSON(await this.request(url, method, body, headers, dPoPOptions));
196
196
  }
197
197
  async fetch(input, init, dPoPOptions) {
198
- return await this.request(input, init?.method, await Req.Body.from(init?.body), Req.Headers.from(init?.headers), dPoPOptions);
198
+ return await this.request(input, init?.method, await requestish.Body.from(init?.body), requestish.Headers.from(init?.headers), dPoPOptions);
199
199
  }
200
200
  async fetchJSON(input, init, dPoPOptions) {
201
201
  return await this.toJSON(await this.fetch(input, init, dPoPOptions));
@@ -1,29 +1,30 @@
1
+ import * as requestish from 'requestish';
1
2
  import * as Req from './Request/index.js';
2
3
  export type OAuth2 = {
3
4
  client_id: string;
4
5
  client_secret: string;
5
- redirect_uri: Req.URL.ish;
6
- authorization_endpoint: Req.URL.ish;
7
- token_endpoint: Req.URL.ish;
6
+ redirect_uri: requestish.URL.ish;
7
+ authorization_endpoint: requestish.URL.ish;
8
+ token_endpoint: requestish.URL.ish;
8
9
  scope?: Req.Scope.ish;
9
10
  };
10
11
  export type OpenID = {
11
- issuer: Req.URL.ish;
12
+ issuer: requestish.URL.ish;
12
13
  client_id: string;
13
14
  client_secret: string;
14
- redirect_uri: Req.URL.ish;
15
+ redirect_uri: requestish.URL.ish;
15
16
  };
16
17
  export type Combined = {
17
18
  client_id: string;
18
19
  client_secret: string;
19
- redirect_uri: Req.URL.ish;
20
+ redirect_uri: requestish.URL.ish;
20
21
  scope?: Req.Scope.ish;
21
22
  } & ({
22
- issuer?: Req.URL.ish;
23
- authorization_endpoint: Req.URL.ish;
24
- token_endpoint: Req.URL.ish;
23
+ issuer?: requestish.URL.ish;
24
+ authorization_endpoint: requestish.URL.ish;
25
+ token_endpoint: requestish.URL.ish;
25
26
  } | {
26
- issuer: Req.URL.ish;
27
- authorization_endpoint?: Req.URL.ish;
28
- token_endpoint?: Req.URL.ish;
27
+ issuer: requestish.URL.ish;
28
+ authorization_endpoint?: requestish.URL.ish;
29
+ token_endpoint?: requestish.URL.ish;
29
30
  });
@@ -1,23 +1,21 @@
1
- import * as Body from './Body.js';
2
- import * as Headers from './Headers.js';
1
+ import * as requestish from 'requestish';
3
2
  import * as Scope from './Scope.js';
4
- import * as URLSearchParams from './URLSearchParams.js';
5
3
  export type Injection = {
6
4
  /**
7
5
  * Search query parameters to include in server request (may be ovewritten by
8
6
  * computed values such as `state` or `challenge_code`)
9
7
  */
10
- search?: URLSearchParams.ish;
8
+ search?: requestish.URLSearchParams.ish;
11
9
  /**
12
10
  * HTTP headers to include in server request (may be overwritten by computed
13
11
  * values such as `Authorization: Bearer <token>`)
14
12
  */
15
- headers?: Headers.ish;
13
+ headers?: requestish.Headers.ish;
16
14
  /**
17
15
  * HTTP request body parameters to include in server request (if request
18
16
  * method allows)
19
17
  */
20
- body?: Body.ish;
18
+ body?: requestish.Body.ish;
21
19
  /** Specific scope or scopes */
22
20
  scope?: Scope.ish;
23
21
  };
@@ -1,6 +1,2 @@
1
- export * as Body from './Body.js';
2
- export * as Headers from './Headers.js';
3
1
  export * from './Injection.js';
4
2
  export * as Scope from './Scope.js';
5
- export * as URL from './URL.js';
6
- export * as URLSearchParams from './URLSearchParams.js';
@@ -1,6 +1,2 @@
1
- export * as Body from './Body.js';
2
- export * as Headers from './Headers.js';
3
1
  export * from './Injection.js';
4
2
  export * as Scope from './Scope.js';
5
- export * as URL from './URL.js';
6
- export * as URLSearchParams from './URLSearchParams.js';
package/dist/Session.d.ts CHANGED
@@ -37,7 +37,7 @@ export declare class Session {
37
37
  */
38
38
  authorizationCodeGrant(): Promise<Token.Response>;
39
39
  /** OAuth 2.0 redirect_uri that this session is handling */
40
- get redirect_uri(): Req.URL.ish;
40
+ get redirect_uri(): import("requestish/dist/URL.js").ish;
41
41
  getAuthorizationUrl(): Promise<string>;
42
42
  /**
43
43
  * Express RequestHandler for the out-of-band redirect in the Authorization
package/dist/WebServer.js CHANGED
@@ -2,8 +2,8 @@ import express from 'express';
2
2
  import * as gcrtl from 'gcrtl';
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
+ import * as requestish from 'requestish';
5
6
  import * as Errors from './Errors/index.js';
6
- import * as Req from './Request/index.js';
7
7
  let ejs = undefined;
8
8
  try {
9
9
  ejs = (await import('ejs')).default;
@@ -28,7 +28,7 @@ export class WebServer {
28
28
  this.session = session;
29
29
  this.authorization_endpoint = authorize_endpoint;
30
30
  this.views = views;
31
- const url = Req.URL.from(this.session.redirect_uri);
31
+ const url = requestish.URL.from(this.session.redirect_uri);
32
32
  this.port = url.port;
33
33
  if (WebServer.activePorts.includes(this.port)) {
34
34
  throw new Errors.PortCollision(url.port);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oauth2-cli",
3
- "version": "0.6.0",
3
+ "version": "0.7.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": {
@@ -23,7 +23,8 @@
23
23
  "oauth4webapi": "^3.8.4",
24
24
  "open": "^11.0.0",
25
25
  "openid-client": "^6.8.2",
26
- "ora": "^9.3.0"
26
+ "ora": "^9.3.0",
27
+ "requestish": "0.1.0"
27
28
  },
28
29
  "devDependencies": {
29
30
  "@battis/descriptive-types": "^0.2.6",
@@ -1,3 +0,0 @@
1
- import { FetchBody } from 'openid-client';
2
- export type ish = FetchBody | FormData | Record<string, string> | RequestInit['body'];
3
- export declare function from(body: ish): Promise<FetchBody | undefined>;
@@ -1,19 +0,0 @@
1
- import { isRecord } from '@battis/typescript-tricks';
2
- function isString(obj) {
3
- return typeof obj === 'string';
4
- }
5
- export async function from(body) {
6
- if (body === undefined ||
7
- body === null ||
8
- typeof body === 'string' ||
9
- body instanceof ArrayBuffer ||
10
- body instanceof ReadableStream ||
11
- body instanceof Uint8Array ||
12
- body instanceof URLSearchParams) {
13
- return body;
14
- }
15
- else if (isRecord(body, isString, isString)) {
16
- return new URLSearchParams(body);
17
- }
18
- return new Response(body).arrayBuffer();
19
- }
@@ -1,3 +0,0 @@
1
- export type ish = Headers | NonNullable<RequestInit['headers']> | Record<string, string> | [string, string][];
2
- export declare function from(headers?: ish): Headers;
3
- export declare function merge(a?: ish, b?: ish): Headers | undefined;
@@ -1,20 +0,0 @@
1
- export function from(headers) {
2
- if (headers instanceof Headers) {
3
- return headers;
4
- }
5
- return new Headers(headers);
6
- }
7
- export function merge(a, b) {
8
- if (a && !b) {
9
- return from(a);
10
- }
11
- else if (!a && b) {
12
- return from(b);
13
- }
14
- else if (a && b) {
15
- const headers = from(a);
16
- from(b).forEach((value, key) => headers.set(key, value));
17
- return headers;
18
- }
19
- return undefined;
20
- }
@@ -1,4 +0,0 @@
1
- import { URLString } from '@battis/descriptive-types';
2
- export type ish = URL | URLString;
3
- export declare function from(url: ish): URL;
4
- export declare function toString(url: ish): URLString;
@@ -1,12 +0,0 @@
1
- export function from(url) {
2
- if (url instanceof URL) {
3
- return url;
4
- }
5
- return new URL(url);
6
- }
7
- export function toString(url) {
8
- if (typeof url === 'string') {
9
- return url;
10
- }
11
- return url.toString();
12
- }
@@ -1,6 +0,0 @@
1
- import { ish as URLish } from './URL.js';
2
- export type ish = URLSearchParams | Record<string, string>;
3
- export declare function from(search: ish): URLSearchParams;
4
- export declare function toString(search: ish): string;
5
- export declare function merge(a?: ish, b?: ish): URLSearchParams | undefined;
6
- export declare function appendTo(url: URLish, search: ish): URLish;
@@ -1,37 +0,0 @@
1
- export function from(search) {
2
- if (search instanceof URLSearchParams) {
3
- return search;
4
- }
5
- return new URLSearchParams(search);
6
- }
7
- export function toString(search) {
8
- const query = from(search).toString();
9
- if (query.length) {
10
- return `?${query}`;
11
- }
12
- return '';
13
- }
14
- export function merge(a, b) {
15
- if (a && !b) {
16
- return from(a);
17
- }
18
- else if (!a && b) {
19
- return from(b);
20
- }
21
- else if (a && b) {
22
- const search = from(a);
23
- from(b).forEach((value, key) => search.set(key, value));
24
- return search;
25
- }
26
- return undefined;
27
- }
28
- export function appendTo(url, search) {
29
- if (url instanceof URL) {
30
- const result = new URL(url);
31
- result.search = toString(search);
32
- return result;
33
- }
34
- else {
35
- return url.replace(/(.+)(\?.*)?$/, `$1${toString(search)}`);
36
- }
37
- }