oauth2-cli 0.8.9 → 1.0.1

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/dist/Client.js CHANGED
@@ -1,15 +1,11 @@
1
1
  import { Mutex } from 'async-mutex';
2
+ import * as gcrtl from 'gcrtl';
2
3
  import { EventEmitter } from 'node:events';
3
4
  import path from 'node:path';
4
5
  import * as OpenIDClient from 'openid-client';
5
- import * as requestish from 'requestish';
6
- import * as Scope from './Scope.js';
7
- import { Session } from './Session.js';
8
- /**
9
- * A generic `redirect_uri` to use if the server does not require pre-registered
10
- * `redirect_uri` values
11
- */
12
- export const DEFAULT_REDIRECT_URI = 'http://localhost:3000/oauth2-cli/redirect';
6
+ import { Body, Headers, URL, URLSearchParams } from 'requestish';
7
+ import * as Localhost from './Localhost/index.js';
8
+ import * as Token from './Token/index.js';
13
9
  /**
14
10
  * Wrap {@link https://www.npmjs.com/package/openid-client openid-client} in a
15
11
  * class instance specific to a particular OAuth/OpenID server credential-set,
@@ -19,158 +15,225 @@ export const DEFAULT_REDIRECT_URI = 'http://localhost:3000/oauth2-cli/redirect';
19
15
  */
20
16
  export class Client extends EventEmitter {
21
17
  static TokenEvent = 'token';
22
- name;
18
+ _name;
19
+ /** Human-readable name for client in messages */
20
+ get name() {
21
+ if (this._name && this._name.length > 0) {
22
+ return this._name;
23
+ }
24
+ return 'API';
25
+ }
26
+ /** Human-readable reason for authorization in messages */
27
+ reason;
28
+ /** Credentials for server access */
23
29
  credentials;
30
+ /** Base URL for all non-absolute requests */
24
31
  base_url;
32
+ /**
33
+ * `openid-client` configuration metadata (either dervied from
34
+ * {@link credentials}) or requested from the well-known OpenID configuration
35
+ * endpoint of the `issuer`
36
+ */
25
37
  config;
38
+ /** Optional request components to inject */
26
39
  inject;
27
- views;
40
+ /** Optional configuration options for web server listening for redirect */
41
+ localhostOptions;
42
+ /** Optional {@link TokenStorage} implementation to manage tokens */
43
+ storage;
44
+ /** Current response to an access token grant request, if available */
28
45
  token;
46
+ /** Mutex preventing multiple simultaneous access token grant requests */
29
47
  tokenLock = new Mutex();
30
- storage;
31
- constructor({ name, credentials, base_url, views, inject, storage }) {
48
+ /** @see {@link Options.Client} */
49
+ constructor({ name, reason, credentials, base_url, inject, storage, localhost }) {
32
50
  super();
33
- this.name = name;
51
+ this._name = name;
52
+ this.reason = reason;
34
53
  this.credentials = credentials;
35
54
  this.base_url = base_url;
36
- this.views = views;
55
+ this.localhostOptions = localhost;
37
56
  this.inject = inject;
38
57
  this.storage = storage;
39
58
  }
40
- clientName() {
41
- if (this.name && this.name.length > 0) {
42
- return this.name;
43
- }
44
- return 'oauth2-cli';
45
- }
46
- get redirect_uri() {
47
- return this.credentials.redirect_uri;
48
- }
49
59
  /**
50
- * @throws IndeterminateConfiguration if provided credentials combined with
51
- * OpenID discovery fail to generate a complete configuration
60
+ * Build a client configuration either via `issuer` discovery or from provided
61
+ * `credentials`
52
62
  */
53
63
  async getConfiguration() {
54
- let error = undefined;
64
+ let discovery = undefined;
55
65
  if (!this.config && this.credentials.issuer) {
56
66
  try {
57
- this.config = await OpenIDClient.discovery(requestish.URL.from(this.credentials.issuer), this.credentials.client_id, { client_secret: this.credentials.client_secret });
67
+ this.config = await OpenIDClient.discovery(URL.from(this.credentials.issuer), this.credentials.client_id, { client_secret: this.credentials.client_secret });
58
68
  }
59
- catch (e) {
60
- error = e;
69
+ catch (error) {
70
+ discovery = error;
61
71
  }
62
72
  }
63
73
  if (!this.config && this.credentials?.authorization_endpoint) {
64
74
  this.config = new OpenIDClient.Configuration({
65
- issuer: `https://${requestish.URL.from(this.credentials.authorization_endpoint).hostname}`,
66
- authorization_endpoint: requestish.URL.toString(this.credentials.authorization_endpoint),
67
- token_endpoint: requestish.URL.toString(this.credentials.token_endpoint ||
75
+ issuer: `https://${URL.from(this.credentials.authorization_endpoint).hostname}`,
76
+ authorization_endpoint: URL.toString(this.credentials.authorization_endpoint),
77
+ token_endpoint: URL.toString(this.credentials.token_endpoint ||
68
78
  this.credentials.authorization_endpoint)
69
79
  }, this.credentials.client_id, { client_secret: this.credentials.client_secret });
70
80
  }
71
81
  if (!this.config) {
72
- throw new Error(`The ${this.clientName()} configuration could not be constructed from provided credentials.`, {
82
+ throw new Error(`Client configuration for ${this.name} could not be discovered or derived from credentials`, {
73
83
  cause: {
74
84
  credentials: this.credentials,
75
- 'OpenID configuration result': error
85
+ discovery
76
86
  }
77
87
  });
78
88
  }
79
89
  return this.config;
80
90
  }
81
- async getParameters(session) {
82
- const params = requestish.URLSearchParams.merge(this.inject?.search, session.inject?.search) || new URLSearchParams();
83
- params.set('redirect_uri', requestish.URL.toString(this.credentials.redirect_uri));
91
+ /**
92
+ * Build a URL to redirect the user-agent to, in order to request
93
+ * authorization at the Authorization Server
94
+ *
95
+ * @param session Contains the current `state` and `code_verifier` for the
96
+ * Authorization Code flow session
97
+ */
98
+ async buildAuthorizationUrl(session) {
99
+ const params = URLSearchParams.from(this.inject?.search);
100
+ params.set('redirect_uri', URL.toString(this.credentials.redirect_uri));
84
101
  params.set('code_challenge', await OpenIDClient.calculatePKCECodeChallenge(session.code_verifier));
85
102
  params.set('code_challenge_method', 'S256');
86
103
  params.set('state', session.state);
87
104
  if (this.credentials.scope) {
88
- params.set('scope', Scope.toString(this.credentials.scope));
105
+ params.set('scope', Token.Scope.toString(this.credentials.scope));
89
106
  }
90
- return params;
91
- }
92
- async getAuthorizationUrl(session) {
93
- return OpenIDClient.buildAuthorizationUrl(await this.getConfiguration(), await this.getParameters(session));
94
- }
95
- createSession({ views, ...options }) {
96
- return new Session({
97
- client: this,
98
- views: views || this.views,
99
- ...options
100
- });
107
+ return OpenIDClient.buildAuthorizationUrl(await this.getConfiguration(), params);
101
108
  }
109
+ /** Does the client hold or have access to an unexpired API access token? */
102
110
  async isAuthorized() {
103
111
  if (this.token?.expiresIn()) {
104
112
  return true;
105
113
  }
106
114
  else {
107
115
  return await this.tokenLock.runExclusive(async () => {
108
- return !!(await this.refreshTokenGrant());
116
+ try {
117
+ return !!(await this.refreshTokenGrant());
118
+ }
119
+ catch (_) {
120
+ return false;
121
+ }
109
122
  });
110
123
  }
111
124
  }
112
- async authorize(options = {}) {
113
- const session = this.createSession(options);
125
+ /** Start interactive authorization for API access with the user */
126
+ async authorize() {
127
+ return await this.tokenLock.runExclusive(async () => {
128
+ console.log('running authorize from external call');
129
+ return await this._authorize();
130
+ });
131
+ }
132
+ /**
133
+ * Start interactive authorization for API access with the user _without_
134
+ * checking for tokenLock mutex
135
+ *
136
+ * Should be called _only_ from within a `tokenLock.runExclusive()` callback
137
+ */
138
+ async _authorize() {
139
+ const session = new Localhost.Server({
140
+ client: this,
141
+ reason: this.reason,
142
+ ...this.localhostOptions
143
+ });
114
144
  const token = await this.save(await session.authorizationCodeGrant());
115
145
  return token;
116
146
  }
117
- async handleAuthorizationCodeRedirect(req, session) {
147
+ /**
148
+ * Validate the authorization response and then executes the !"Authorization
149
+ * Code Grant" at the Authorization Server's token endpoint to obtain an
150
+ * access token. ID Token and Refresh Token are also optionally issued by the
151
+ * server.
152
+ *
153
+ * @param request Authorization Server's request to the Localhost redirect
154
+ * server
155
+ * @param session Contains the current `state` and `code_verifier` for the
156
+ * Authorization Code flow session
157
+ */
158
+ async handleAuthorizationCodeRedirect(request, session) {
118
159
  try {
119
- /**
120
- * Do _NOT_ await this promise: the WebServer needs to send the
121
- * authorization complete response asynchronously before this can resolve,
122
- * and awaiting session.resolve() will block that response.
123
- */
124
- session.resolve(await OpenIDClient.authorizationCodeGrant(await this.getConfiguration(), new URL(req.url, this.redirect_uri), {
160
+ return await OpenIDClient.authorizationCodeGrant(await this.getConfiguration(), gcrtl.expand(request.url, this.credentials.redirect_uri), {
125
161
  pkceCodeVerifier: session.code_verifier,
126
162
  expectedState: session.state
127
163
  }, this.inject?.search
128
- ? requestish.URLSearchParams.from(this.inject.search)
129
- : undefined));
164
+ ? URLSearchParams.from(this.inject.search)
165
+ : undefined);
130
166
  }
131
167
  catch (cause) {
132
- session.reject(new Error(`Error making ${this.clientName()} Authorization Code Grant request`, { cause }));
168
+ throw new Error(`${this.name} authorization code grant failed.`, {
169
+ cause
170
+ });
133
171
  }
134
172
  }
135
- async refreshTokenGrant({ refresh_token = this.token?.refresh_token, inject: request } = {}) {
173
+ /**
174
+ * Perform an OAuth 2.0 Refresh Token Grant at the Authorization Server's
175
+ * token endpoint, allowing the client to obtain a new access token using a
176
+ * valid `refresh_token`.
177
+ *
178
+ * @see {@link Options.Refresh}
179
+ */
180
+ async refreshTokenGrant({ refresh_token = this.token?.refresh_token, inject } = {}) {
136
181
  if (!refresh_token && !this.token && this.storage) {
137
182
  refresh_token = await this.storage.load();
138
183
  }
139
184
  if (!refresh_token || refresh_token === '') {
140
185
  return undefined;
141
186
  }
142
- const token = await OpenIDClient.refreshTokenGrant(await this.getConfiguration(), refresh_token, this.inject?.search
143
- ? requestish.URLSearchParams.from(this.inject.search)
144
- : undefined, {
145
- // @ts-expect-error 2322 undocumented arg pass-through to oauth4webapi
146
- headers: requestish.Headers.merge(this.headers, request?.headers)
147
- });
148
- return await this.save(token);
187
+ try {
188
+ const token = await OpenIDClient.refreshTokenGrant(await this.getConfiguration(), refresh_token, this.inject?.search
189
+ ? URLSearchParams.from(this.inject.search)
190
+ : undefined, {
191
+ // @ts-expect-error 2322 undocumented arg pass-through to oauth4webapi
192
+ headers: Headers.merge(this.headers, inject?.headers)
193
+ });
194
+ return await this.save(token);
195
+ }
196
+ catch (cause) {
197
+ throw new Error(`Could not refresh access to ${this.name}`, { cause });
198
+ }
149
199
  }
150
200
  /**
151
201
  * Get an unexpired access token
152
202
  *
153
203
  * Depending on provided and/or stored access token and refresh token values,
154
204
  * this may require interactive authorization
205
+ *
206
+ * @see {@link Options.GetToken}
155
207
  */
156
208
  async getToken({ token, inject: request } = {}) {
157
209
  return await this.tokenLock.runExclusive(async () => {
158
210
  token = token || this.token;
159
211
  if (!this.token?.expiresIn() && this.storage) {
160
- this.token = await this.refreshTokenGrant({ inject: request });
212
+ try {
213
+ this.token = await this.refreshTokenGrant({ inject: request });
214
+ }
215
+ catch (_) {
216
+ // token definitely expired and refrehing it failed
217
+ this.token = undefined;
218
+ }
161
219
  }
162
220
  if (!this.token) {
163
- this.token = await this.authorize({ inject: request });
221
+ this.token = await this._authorize();
164
222
  }
165
223
  return this.token;
166
224
  });
167
225
  }
168
- /** @throws MissingAccessToken If response does not include `access_token` */
169
- async save(token) {
170
- this.token = token;
171
- if (!token.access_token) {
172
- throw new Error(`No access_token in response to ${this.clientName()}.`, {
173
- cause: token
226
+ /**
227
+ * Persist `refresh_token` if Token.Storage is configured and `refresh_token`
228
+ * provided
229
+ *
230
+ * @throws If `response` does not include `access_token` property
231
+ */
232
+ async save(response) {
233
+ this.token = response;
234
+ if (!response.access_token) {
235
+ throw new Error(`${this.name} token response does not contain access_token`, {
236
+ cause: response
174
237
  });
175
238
  }
176
239
  if (this.storage && this.token.refresh_token) {
@@ -180,25 +243,30 @@ export class Client extends EventEmitter {
180
243
  return this.token;
181
244
  }
182
245
  /**
183
- * @param url If an `issuer` has been defined, `url` accepts paths relative to
184
- * the `issuer` URL as well as absolute URLs
246
+ * Request a protected resource using the client's access token.
247
+ *
248
+ * This ensures that the access token is unexpired, and interactively requests
249
+ * user authorization if it has not yet been provided.
250
+ *
251
+ * @param url If an `base_url` or `issuer` has been defined, `url` accepts
252
+ * paths relative to the `issuer` URL as well as absolute URLs
185
253
  * @param method Optional, defaults to `GET` unless otherwise specified
186
254
  * @param body Optional
187
255
  * @param headers Optional
188
- * @param dPoPOptions Optional
256
+ * @param dPoPOptions Optional, see {@link OpenIDClient.DPoPOptions}
189
257
  */
190
258
  async request(url, method = 'GET', body, headers = {}, dPoPOptions) {
191
259
  try {
192
- url = requestish.URL.from(url);
260
+ url = URL.from(url);
193
261
  }
194
262
  catch (error) {
195
263
  if (this.base_url || this.credentials.issuer) {
196
264
  url = path.join(
197
265
  // @ts-expect-error 2345 TS, I _just_ tested this!
198
- requestish.URL.toString(this.base_url || this.credentials.issuer), requestish.URL.toString(url).replace(/^\/?/, ''));
266
+ URL.toString(this.base_url || this.credentials.issuer), URL.toString(url).replace(/^\/?/, ''));
199
267
  }
200
268
  else {
201
- throw new Error(`Invalid request URL "${url}" to ${this.clientName()}`, {
269
+ throw new Error(`${this.name} request url invalid`, {
202
270
  cause: {
203
271
  base_url: this.base_url,
204
272
  issuer: this.credentials.issuer,
@@ -207,29 +275,36 @@ export class Client extends EventEmitter {
207
275
  });
208
276
  }
209
277
  }
210
- 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);
278
+ const request = async () => await OpenIDClient.fetchProtectedResource(await this.getConfiguration(), (await this.getToken()).access_token, URL.from(URLSearchParams.appendTo(url, this.inject?.search || {})), method, body, Headers.merge(this.inject?.headers, headers), dPoPOptions);
211
279
  try {
212
280
  return await request();
213
281
  }
214
- catch (error) {
215
- if (typeof error === 'object' &&
216
- error !== null &&
217
- 'status' in error &&
218
- error.status === 401) {
282
+ catch (cause) {
283
+ if (Error.isError(cause) && 'status' in cause && cause.status === 401) {
219
284
  await this.authorize();
220
285
  return await request();
221
286
  }
222
287
  else {
223
- throw error;
288
+ throw new Error(`${this.name} request failed`, { cause });
224
289
  }
225
290
  }
226
291
  }
292
+ /** Parse a fetch response as JSON, typing it as J */
227
293
  async toJSON(response) {
228
294
  if (response.ok) {
229
- return (await response.json());
295
+ try {
296
+ return (await response.json());
297
+ }
298
+ catch (cause) {
299
+ throw new Error(`${this.name} response could not be parsed as JSON`, {
300
+ cause
301
+ });
302
+ }
230
303
  }
231
304
  else {
232
- throw new Error(`The response could not be parsed as JSON by ${this.clientName()}.`, { cause: { response } });
305
+ throw new Error(`${this.name} response status not ok`, {
306
+ cause: { response }
307
+ });
233
308
  }
234
309
  }
235
310
  /**
@@ -239,9 +314,25 @@ export class Client extends EventEmitter {
239
314
  async requestJSON(url, method = 'GET', body, headers = {}, dPoPOptions) {
240
315
  return await this.toJSON(await this.request(url, method, body, headers, dPoPOptions));
241
316
  }
317
+ /**
318
+ * Request a protected resource using the client's access token.
319
+ *
320
+ * This ensures that the access token is unexpired, and interactively requests
321
+ * user authorization if it has not yet been provided.
322
+ *
323
+ * @param input If a `base_url` or `issuer` has been defined, `url` accepts
324
+ * paths relative to the `issuer` URL as well as absolute URLs
325
+ * @param init Optional
326
+ * @param dPoPOptions Optional, see {@link OpenIDClient.DPoPOptions}
327
+ * @see {@link request} for which this is an alias for {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API Fetch API}-style requests
328
+ */
242
329
  async fetch(input, init, dPoPOptions) {
243
- return await this.request(input, init?.method, await requestish.Body.from(init?.body), requestish.Headers.from(init?.headers), dPoPOptions);
330
+ return await this.request(input, init?.method, await Body.from(init?.body), Headers.from(init?.headers), dPoPOptions);
244
331
  }
332
+ /**
333
+ * Returns the result of {@link fetch} as a parsed JSON object, optionally
334
+ * typed as `J`
335
+ */
245
336
  async fetchJSON(input, init, dPoPOptions) {
246
337
  return await this.toJSON(await this.fetch(input, init, dPoPOptions));
247
338
  }
@@ -1,16 +1,48 @@
1
- import * as requestish from 'requestish';
2
- import * as Scope from './Scope.js';
1
+ import { URL } from 'requestish';
2
+ import { Scope } from './Token/index.js';
3
3
  export type Credentials = {
4
+ /** OAuth 2.0 / OpenID Connect `client_id` value */
4
5
  client_id: string;
6
+ /** OAuth 2.0 / OpenID Connect `client_secret` value */
5
7
  client_secret: string;
6
- redirect_uri: requestish.URL.ish;
8
+ /**
9
+ * OAuth 2.0 / OpenID Connect `redirect_uri` value
10
+ *
11
+ * This must either be a URL of the form `http://localhost` or redirect to
12
+ * `http://localhost` for the client to be able to provide support for it. If
13
+ * SSL encryption is required, you must provide that configuration
14
+ * independently.
15
+ */
16
+ redirect_uri: URL.ish;
17
+ /** OAuth 2.0 / OpenID Connect access `scope` value */
7
18
  scope?: Scope.ish;
8
19
  } & ({
9
- issuer?: requestish.URL.ish;
10
- authorization_endpoint: requestish.URL.ish;
11
- token_endpoint: requestish.URL.ish;
20
+ /**
21
+ * Optional OpenID Connect `issuer` to use for the well-known URL
22
+ * discovery process
23
+ */
24
+ issuer?: URL.ish;
25
+ /**
26
+ * OAuth 2.0 `authorization_endpoint` URL to for during Authorization Code
27
+ * flow
28
+ */
29
+ authorization_endpoint: URL.ish;
30
+ /**
31
+ * OAuth 2.0 `authorization_endpoint` URL to for during Authorization Code
32
+ * flow
33
+ */
34
+ token_endpoint: URL.ish;
12
35
  } | {
13
- issuer: requestish.URL.ish;
14
- authorization_endpoint?: requestish.URL.ish;
15
- token_endpoint?: requestish.URL.ish;
36
+ /** OpenID Connect `issuer` to use for the well-known URL discovery process */
37
+ issuer: URL.ish;
38
+ /**
39
+ * Optional OAuth 2.0 `authorization_endpoint` URL to for during
40
+ * Authorization Code flow
41
+ */
42
+ authorization_endpoint?: URL.ish;
43
+ /**
44
+ * Optional OAuth 2.0 `authorization_endpoint` URL to for during
45
+ * Authorization Code flow
46
+ */
47
+ token_endpoint?: URL.ish;
16
48
  });
@@ -1,21 +1,22 @@
1
- import * as requestish from 'requestish';
2
- import * as Scope from './Scope.js';
1
+ import { Body, Headers, URLSearchParams } from 'requestish';
2
+ import { Scope } from './Token/index.js';
3
+ /** Request components to inject when communicating with the API server */
3
4
  export type Injection = {
4
5
  /**
5
6
  * Search query parameters to include in server request (may be ovewritten by
6
7
  * computed values such as `state` or `challenge_code`)
7
8
  */
8
- search?: requestish.URLSearchParams.ish;
9
+ search?: URLSearchParams.ish;
9
10
  /**
10
11
  * HTTP headers to include in server request (may be overwritten by computed
11
12
  * values such as `Authorization: Bearer <token>`)
12
13
  */
13
- headers?: requestish.Headers.ish;
14
+ headers?: Headers.ish;
14
15
  /**
15
16
  * HTTP request body parameters to include in server request (if request
16
17
  * method allows)
17
18
  */
18
- body?: requestish.Body.ish;
19
+ body?: Body.ish;
19
20
  /** Specific scope or scopes */
20
21
  scope?: Scope.ish;
21
22
  };
@@ -0,0 +1 @@
1
+ export declare const DEFAULT_LAUNCH_ENDPOINT = "/oauth2-cli/authorize";
@@ -0,0 +1 @@
1
+ export const DEFAULT_LAUNCH_ENDPOINT = '/oauth2-cli/authorize';
@@ -0,0 +1,28 @@
1
+ import { PathString } from '@battis/descriptive-types';
2
+ import { Client } from '../index.js';
3
+ export type Options = {
4
+ client: Client;
5
+ /** Human-readable name of the app requesting API authorization */
6
+ reason?: string;
7
+ /** See {@link Session.setViews setViews()} */
8
+ views?: PathString;
9
+ /**
10
+ * Local web server launch endpoint
11
+ *
12
+ * This is separate and distinct from the OpenID/OAuth server's authorization
13
+ * endpoint. This endpoint is the first path that the user is directed to in
14
+ * their browser. It can present an explanation of what is being authorized
15
+ * and why. By default it redirects to the OpenID/OAuth server's authorization
16
+ * URL, the first step in the Authorization Code Grant flow.
17
+ */
18
+ launch_endpoint?: PathString;
19
+ /**
20
+ * The number of milliseconds of inactivity before a socket is presumed to
21
+ * have timed out. This can be reduced to limit potential wait times during
22
+ * interactive authentication, but must still be long enough to allow time for
23
+ * the authorization code to be exchanged for an access token.
24
+ *
25
+ * Defaults to 1000 milliseconds
26
+ */
27
+ timeout?: number;
28
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,59 @@
1
+ import { PathString } from '@battis/descriptive-types';
2
+ import { Request, Response } from 'express';
3
+ import * as Token from '../Token/index.js';
4
+ import { Options } from './Options.js';
5
+ /**
6
+ * Minimal HTTP server running on localhost to handle the redirect step of
7
+ * OpenID/OAuth flows
8
+ */
9
+ export declare class Server {
10
+ private static activePorts;
11
+ /** PKCE code_verifier */
12
+ readonly code_verifier: string;
13
+ /** OAuth 2.0 state (if PKCE is not supported) */
14
+ readonly state: string;
15
+ private client;
16
+ private reason;
17
+ private views?;
18
+ private packageViews;
19
+ private spinner;
20
+ protected readonly port: string;
21
+ readonly launch_endpoint: PathString;
22
+ private server;
23
+ private resolveAuthorizationCodeFlow?;
24
+ private rejectAuthorizationCodeFlow?;
25
+ constructor({ reason, client, views, launch_endpoint, timeout }: Options);
26
+ authorizationCodeGrant(): Promise<Token.Response>;
27
+ /**
28
+ * Set the path to folder of *.ejs templates
29
+ *
30
+ * Expected templates include:
31
+ *
32
+ * - `launch.ejs` presents information prior to the authorization to the user,
33
+ * and the user must follow `authorize_url` data property to interactively
34
+ * launch authorization
35
+ * - `complete.ejs` presented to user upon successful completion of
36
+ * authorization flow
37
+ * - `error.ejs` presented to user upon receipt of an error from the server,
38
+ * includes `error` as data
39
+ *
40
+ * `complete.ejs` and `error.ejs` are included with oauth2-cli and those
41
+ * templates will be used if `ejs` is imported but no replacement templates
42
+ * are found.
43
+ *
44
+ * All views receive a data property `name` which is the human-readable name
45
+ * of the Client managing the authorization code flow, and `reason` indicating
46
+ * the human-readable reason (i.e. name of the app) requesting authorization,
47
+ * which should be displayed in messages for transparency.
48
+ *
49
+ * @param views Should be an absolute path
50
+ */
51
+ setViews(views: PathString): void;
52
+ protected render(res: Response, template: string, data?: Record<string, unknown>): Promise<boolean>;
53
+ /** Handles request to `/authorize` */
54
+ protected handleAuthorizationEndpoint(req: Request, res: Response): Promise<void>;
55
+ /** Handles request to `redirect_uri` */
56
+ protected handleRedirect(req: Request, res: Response): Promise<void>;
57
+ /** Shut down web server */
58
+ close(): Promise<void>;
59
+ }