oauth2-cli 0.1.3 → 0.1.4

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,20 @@
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.1.4](https://github.com/battis/oauth2-cli/compare/oauth2-cli/0.1.3...oauth2-cli/0.1.4) (2025-03-06)
6
+
7
+
8
+ ### Features
9
+
10
+ * **oauth2-cli:** cache token in memory ([68ac632](https://github.com/battis/oauth2-cli/commit/68ac6323031cbcaa0dd7b444dcd6da62b4f9a48d))
11
+ * **oauth2-cli:** Client.request() and Client.requestJSON() ([50c1198](https://github.com/battis/oauth2-cli/commit/50c11985c0ae8f135932d05bae2bf74ff1cd29df))
12
+ * **oauth2-cli:** deprecate TokenManager (replaced by Client) ([991ac42](https://github.com/battis/oauth2-cli/commit/991ac42eb2cc83b4b31e60856faf192233cd35f3))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * **oauth2-cli:** improve error window title ([97a4c1c](https://github.com/battis/oauth2-cli/commit/97a4c1c9f98aaacf7ce63fb05a64cfee5f4dd0ce))
18
+
5
19
  ## [0.1.3](https://github.com/battis/oauth2-cli/compare/oauth2-cli/0.1.2...oauth2-cli/0.1.3) (2025-03-06)
6
20
 
7
21
  ### Patch Changes
package/README.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  Acquire API access tokens via OAuth 2.0 within CLI tools
4
4
 
5
+ [![npm version](https://badge.fury.io/js/oauth2-cli.svg)](https://badge.fury.io/js/oauth2-cli)
6
+ [![Module type: ESM](https://img.shields.io/badge/module%20type-esm-brightgreen)](https://nodejs.org/api/esm.html)
7
+
5
8
  ## Install
6
9
 
7
10
  ```sh
@@ -1,4 +1,5 @@
1
1
  import * as Configuration from '@battis/oauth2-configure';
2
+ import * as OpenIDClient from 'openid-client';
2
3
  import { Token } from './Token.js';
3
4
  import { TokenStorage } from './TokenStorage.js';
4
5
  export type Options = Configuration.Options & {
@@ -7,12 +8,17 @@ export type Options = Configuration.Options & {
7
8
  parameters?: Record<string, string>;
8
9
  store?: TokenStorage | string;
9
10
  };
10
- export declare class TokenManager {
11
+ export declare class Client {
11
12
  private options;
12
13
  private tokenMutex;
14
+ private config?;
15
+ private token?;
13
16
  private store?;
14
17
  constructor(options: Options);
15
18
  getToken(): Promise<Token | undefined>;
16
19
  private refreshToken;
20
+ private getConfiguration;
17
21
  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>;
18
24
  }
package/dist/Client.js ADDED
@@ -0,0 +1,97 @@
1
+ import * as Configuration from '@battis/oauth2-configure';
2
+ import { Mutex } from 'async-mutex';
3
+ import * as OpenIDClient from 'openid-client';
4
+ import { FileStorage } from './FileStorage.js';
5
+ import * as Localhost from './Localhost.js';
6
+ import { Token } from './Token.js';
7
+ export class Client {
8
+ options;
9
+ tokenMutex = new Mutex();
10
+ config;
11
+ token;
12
+ 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);
18
+ }
19
+ else {
20
+ this.store = this.options.store;
21
+ }
22
+ }
23
+ }
24
+ async getToken() {
25
+ return await this.tokenMutex.runExclusive((async () => {
26
+ if (!this.token) {
27
+ this.token = await this.store?.load();
28
+ }
29
+ if (this.token?.hasExpired()) {
30
+ this.token = await this.refreshToken(this.token);
31
+ }
32
+ if (!this.token) {
33
+ this.token = await this.authorize();
34
+ }
35
+ return this.token;
36
+ }).bind(this));
37
+ }
38
+ async refreshToken(token) {
39
+ if (token.refresh_token) {
40
+ const { headers, parameters } = this.options;
41
+ let freshTokens;
42
+ if ((freshTokens = Token.fromResponse(await OpenIDClient.refreshTokenGrant(await Configuration.acquire(this.options), token.refresh_token, parameters,
43
+ // @ts-expect-error 2322 undocumented arg pass-through to oauth4webapi
44
+ { headers })))) {
45
+ return this.store?.save(freshTokens) || freshTokens;
46
+ }
47
+ }
48
+ return this.authorize();
49
+ }
50
+ async getConfiguration() {
51
+ if (!this.config) {
52
+ this.config = await Configuration.acquire(this.options);
53
+ }
54
+ return this.config;
55
+ }
56
+ async authorize() {
57
+ const { scope, redirect_uri, parameters: additionalParameters } = this.options;
58
+ return new Promise(async (resolve, reject) => {
59
+ 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
88
+ });
89
+ });
90
+ }
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);
93
+ }
94
+ async requestJSON(url, method = 'GET', body, headers, options) {
95
+ return await (await this.request(url, method, body, headers, options)).json();
96
+ }
97
+ }
@@ -1,12 +1,13 @@
1
1
  import * as Configuration from '@battis/oauth2-configure';
2
+ import * as OpenIDClient from 'openid-client';
2
3
  type Options = Configuration.Options & {
3
4
  authorization_url: string;
4
5
  redirect_uri: string;
5
6
  headers?: Record<string, string>;
6
7
  code_verifier?: string;
7
8
  state?: string;
8
- resolve: Function;
9
- reject: Function;
9
+ resolve: (tokens?: OpenIDClient.TokenEndpointResponse) => void;
10
+ reject: (error: unknown) => void;
10
11
  views?: string;
11
12
  };
12
13
  export declare function redirectServer(options: Options): Promise<void>;
package/dist/Localhost.js CHANGED
@@ -3,7 +3,7 @@ import express from 'express';
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
5
  import open from 'open';
6
- import * as client from 'openid-client';
6
+ import * as OpenIDClient from 'openid-client';
7
7
  export async function redirectServer(options) {
8
8
  const { authorization_url, redirect_uri, code_verifier, state, headers, resolve, reject, views = '../views' } = options;
9
9
  const redirectUrl = new URL(redirect_uri);
@@ -16,7 +16,7 @@ export async function redirectServer(options) {
16
16
  let tokens = undefined;
17
17
  let error = undefined;
18
18
  app.get('/authorize', async (req, res) => {
19
- let viewPath = path.resolve(import.meta.dirname, views, 'authorize');
19
+ const viewPath = path.resolve(import.meta.dirname, views, 'authorize');
20
20
  if (ejs && fs.existsSync(viewPath)) {
21
21
  res.send(await ejs.renderFile(viewPath));
22
22
  }
@@ -27,11 +27,11 @@ export async function redirectServer(options) {
27
27
  app.get(redirectUrl.pathname, async (req, res) => {
28
28
  try {
29
29
  const currentUrl = new URL(req.originalUrl, redirect_uri);
30
- tokens = await client.authorizationCodeGrant(configuration, currentUrl, {
30
+ tokens = await OpenIDClient.authorizationCodeGrant(configuration, currentUrl, {
31
31
  pkceCodeVerifier: code_verifier,
32
32
  expectedState: state
33
33
  },
34
- // @ts-ignore FIXME undocumented arg pass-through to oauth4webapi
34
+ // @ts-expect-error 2322 undocumented arg pass-through to oauth4webapi
35
35
  { headers });
36
36
  }
37
37
  catch (e) {
package/dist/Token.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import * as client from 'openid-client';
2
- interface TokenResponse extends client.TokenEndpointResponse {
1
+ import * as OpenIDClient from 'openid-client';
2
+ interface TokenResponse extends OpenIDClient.TokenEndpointResponse {
3
3
  [key: string]: any;
4
4
  }
5
5
  export declare class Token implements TokenResponse {
@@ -12,7 +12,7 @@ export declare class Token implements TokenResponse {
12
12
  readonly id_token?: string;
13
13
  readonly expires_in?: number;
14
14
  private constructor();
15
- static fromResponse(response?: client.TokenEndpointResponse): Token | undefined;
15
+ static fromResponse(response?: OpenIDClient.TokenEndpointResponse): Token | undefined;
16
16
  hasExpired(): boolean;
17
17
  }
18
18
  export {};
package/dist/index.d.ts CHANGED
@@ -1,4 +1,8 @@
1
+ import { Client } from './Client.js';
2
+ export { Client } from './Client.js';
1
3
  export { FileStorage } from './FileStorage.js';
2
4
  export { Token } from './Token.js';
3
- export { TokenManager } from './TokenManager.js';
4
5
  export { TokenStorage } from './TokenStorage.js';
6
+ export {
7
+ /** @deprecated use Client */
8
+ Client as TokenManager };
package/dist/index.js CHANGED
@@ -1,3 +1,7 @@
1
+ import { Client } from './Client.js';
2
+ export { Client } from './Client.js';
1
3
  export { FileStorage } from './FileStorage.js';
2
4
  export { Token } from './Token.js';
3
- export { TokenManager } from './TokenManager.js';
5
+ export {
6
+ /** @deprecated use Client */
7
+ Client as TokenManager };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oauth2-cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
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": {
@@ -19,7 +19,7 @@
19
19
  "async-mutex": "^0.5.0",
20
20
  "express": "^4.21.2",
21
21
  "open": "^10.1.0",
22
- "openid-client": "^6.1.7",
22
+ "openid-client": "^6.3.3",
23
23
  "@battis/oauth2-configure": "0.1.1"
24
24
  },
25
25
  "devDependencies": {
@@ -29,7 +29,7 @@
29
29
  "commit-and-tag-version": "^12.5.0",
30
30
  "del-cli": "^6.0.0",
31
31
  "npm-run-all": "^4.1.5",
32
- "typescript": "^5.7.2"
32
+ "typescript": "^5.8.2"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "ejs": "*"
@@ -1,6 +1,6 @@
1
1
  import * as Configuration from '@battis/oauth2-configure';
2
2
  import { Mutex } from 'async-mutex';
3
- import * as client from 'openid-client';
3
+ 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';
@@ -13,8 +13,10 @@ export type Options = Configuration.Options & {
13
13
  store?: TokenStorage | string;
14
14
  };
15
15
 
16
- export class TokenManager {
16
+ export class Client {
17
17
  private tokenMutex = new Mutex();
18
+ private config?: OpenIDClient.Configuration;
19
+ private token?: Token;
18
20
  private store?: TokenStorage;
19
21
 
20
22
  public constructor(private options: Options) {
@@ -30,11 +32,16 @@ export class TokenManager {
30
32
  public async getToken() {
31
33
  return await this.tokenMutex.runExclusive(
32
34
  (async () => {
33
- let token = await this.store?.load();
34
- if (token?.hasExpired()) {
35
- token = await this.refreshToken(token);
35
+ if (!this.token) {
36
+ this.token = await this.store?.load();
36
37
  }
37
- return token || (await this.authorize());
38
+ if (this.token?.hasExpired()) {
39
+ this.token = await this.refreshToken(this.token);
40
+ }
41
+ if (!this.token) {
42
+ this.token = await this.authorize();
43
+ }
44
+ return this.token;
38
45
  }).bind(this)
39
46
  );
40
47
  }
@@ -45,11 +52,11 @@ export class TokenManager {
45
52
  let freshTokens;
46
53
  if (
47
54
  (freshTokens = Token.fromResponse(
48
- await client.refreshTokenGrant(
55
+ await OpenIDClient.refreshTokenGrant(
49
56
  await Configuration.acquire(this.options),
50
57
  token.refresh_token,
51
58
  parameters,
52
- // @ts-ignore FIXME undocumented arg pass-through to oauth4webapi
59
+ // @ts-expect-error 2322 undocumented arg pass-through to oauth4webapi
53
60
  { headers }
54
61
  )
55
62
  ))
@@ -60,6 +67,13 @@ export class TokenManager {
60
67
  return this.authorize();
61
68
  }
62
69
 
70
+ private async getConfiguration() {
71
+ if (!this.config) {
72
+ this.config = await Configuration.acquire(this.options);
73
+ }
74
+ return this.config;
75
+ }
76
+
63
77
  private async authorize(): Promise<Token | undefined> {
64
78
  const {
65
79
  scope,
@@ -68,10 +82,9 @@ export class TokenManager {
68
82
  } = this.options;
69
83
 
70
84
  return new Promise(async (resolve, reject) => {
71
- const configuration = await Configuration.acquire(this.options);
72
- const code_verifier = client.randomPKCECodeVerifier();
85
+ const code_verifier = OpenIDClient.randomPKCECodeVerifier();
73
86
  const code_challenge =
74
- await client.calculatePKCECodeChallenge(code_verifier);
87
+ await OpenIDClient.calculatePKCECodeChallenge(code_verifier);
75
88
  let state: string | undefined = undefined;
76
89
  const parameters: Record<string, string> = {
77
90
  ...additionalParameters,
@@ -83,28 +96,58 @@ export class TokenManager {
83
96
  if (scope) {
84
97
  parameters.scope = scope;
85
98
  }
86
- if (!configuration.serverMetadata().supportsPKCE()) {
87
- state = client.randomState();
99
+ if (!(await this.getConfiguration()).serverMetadata().supportsPKCE()) {
100
+ state = OpenIDClient.randomState();
88
101
  parameters.state = state;
89
102
  }
90
103
 
91
104
  await Localhost.redirectServer({
92
105
  ...this.options,
93
- authorization_url: client.buildAuthorizationUrl(
94
- configuration,
106
+ authorization_url: OpenIDClient.buildAuthorizationUrl(
107
+ await this.getConfiguration(),
95
108
  parameters
96
109
  ).href,
97
110
  code_verifier,
98
111
  state,
99
- resolve: (async (response: client.TokenEndpointResponse) => {
100
- const token = Token.fromResponse(response);
101
- if (token && this.store) {
102
- await this.store.save(token);
112
+ resolve: (async (response?: OpenIDClient.TokenEndpointResponse) => {
113
+ this.token = Token.fromResponse(response);
114
+ if (this.token && this.store) {
115
+ await this.store.save(this.token);
103
116
  }
104
- resolve(token);
117
+ resolve(this.token);
105
118
  }).bind(this),
106
119
  reject
107
120
  });
108
121
  });
109
122
  }
123
+
124
+ public async request(
125
+ url: URL | string,
126
+ method: string = 'GET',
127
+ body?: OpenIDClient.FetchBody,
128
+ headers?: Record<string, string>,
129
+ options?: OpenIDClient.DPoPOptions
130
+ ) {
131
+ return await OpenIDClient.fetchProtectedResource(
132
+ await this.getConfiguration(),
133
+ (await this.getToken())!.access_token,
134
+ new URL(url),
135
+ method,
136
+ body,
137
+ new Headers({ ...this.options.headers, ...headers }),
138
+ options
139
+ );
140
+ }
141
+
142
+ public async requestJSON(
143
+ url: URL | string,
144
+ method: string = 'GET',
145
+ body?: OpenIDClient.FetchBody,
146
+ headers?: Record<string, string>,
147
+ options?: OpenIDClient.DPoPOptions
148
+ ) {
149
+ return await (
150
+ await this.request(url, method, body, headers, options)
151
+ ).json();
152
+ }
110
153
  }
package/src/Localhost.ts CHANGED
@@ -3,7 +3,7 @@ import express from 'express';
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
5
  import open from 'open';
6
- import * as client from 'openid-client';
6
+ import * as OpenIDClient from 'openid-client';
7
7
 
8
8
  type Options = Configuration.Options & {
9
9
  authorization_url: string;
@@ -11,8 +11,8 @@ type Options = Configuration.Options & {
11
11
  headers?: Record<string, string>;
12
12
  code_verifier?: string;
13
13
  state?: string;
14
- resolve: Function;
15
- reject: Function;
14
+ resolve: (tokens?: OpenIDClient.TokenEndpointResponse) => void;
15
+ reject: (error: unknown) => void;
16
16
  views?: string;
17
17
  };
18
18
 
@@ -36,11 +36,11 @@ export async function redirectServer(options: Options) {
36
36
  const server = app.listen(port);
37
37
  const ejs = await import('ejs');
38
38
  let view = 'complete.ejs';
39
- let tokens: client.TokenEndpointResponse | undefined = undefined;
40
- let error: any = undefined;
39
+ let tokens: OpenIDClient.TokenEndpointResponse | undefined = undefined;
40
+ let error: unknown = undefined;
41
41
 
42
42
  app.get('/authorize', async (req, res) => {
43
- let viewPath = path.resolve(import.meta.dirname, views, 'authorize');
43
+ const viewPath = path.resolve(import.meta.dirname, views, 'authorize');
44
44
  if (ejs && fs.existsSync(viewPath)) {
45
45
  res.send(await ejs.renderFile(viewPath));
46
46
  } else {
@@ -50,14 +50,14 @@ export async function redirectServer(options: Options) {
50
50
  app.get(redirectUrl.pathname, async (req, res) => {
51
51
  try {
52
52
  const currentUrl = new URL(req.originalUrl, redirect_uri);
53
- tokens = await client.authorizationCodeGrant(
53
+ tokens = await OpenIDClient.authorizationCodeGrant(
54
54
  configuration,
55
55
  currentUrl,
56
56
  {
57
57
  pkceCodeVerifier: code_verifier,
58
58
  expectedState: state
59
59
  },
60
- // @ts-ignore FIXME undocumented arg pass-through to oauth4webapi
60
+ // @ts-expect-error 2322 undocumented arg pass-through to oauth4webapi
61
61
  { headers }
62
62
  );
63
63
  } catch (e) {
package/src/Token.ts CHANGED
@@ -1,6 +1,6 @@
1
- import * as client from 'openid-client';
1
+ import * as OpenIDClient from 'openid-client';
2
2
 
3
- interface TokenResponse extends client.TokenEndpointResponse {
3
+ interface TokenResponse extends OpenIDClient.TokenEndpointResponse {
4
4
  [key: string]: any;
5
5
  }
6
6
 
@@ -14,14 +14,14 @@ export class Token implements TokenResponse {
14
14
  public readonly id_token?: string;
15
15
  public readonly expires_in?: number;
16
16
 
17
- private constructor(response: client.TokenEndpointResponse) {
17
+ private constructor(response: OpenIDClient.TokenEndpointResponse) {
18
18
  this.access_token = response.access_token;
19
19
  this.token_type = response.token_type;
20
20
  this.timestamp = response.timestamp;
21
21
  Object.assign(this, response);
22
22
  }
23
23
 
24
- public static fromResponse(response?: client.TokenEndpointResponse) {
24
+ public static fromResponse(response?: OpenIDClient.TokenEndpointResponse) {
25
25
  if (response) {
26
26
  return new Token({ timestamp: Date.now(), ...response });
27
27
  }
package/src/index.ts CHANGED
@@ -1,4 +1,11 @@
1
+ import { Client } from './Client.js';
2
+
3
+ export { Client } from './Client.js';
1
4
  export { FileStorage } from './FileStorage.js';
2
5
  export { Token } from './Token.js';
3
- export { TokenManager } from './TokenManager.js';
4
6
  export { TokenStorage } from './TokenStorage.js';
7
+
8
+ export {
9
+ /** @deprecated use Client */
10
+ Client as TokenManager
11
+ };
package/views/error.ejs CHANGED
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>Authorization Complete</title>
6
+ <title>Authorization Error</title>
7
7
  <link
8
8
  href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
9
9
  rel="stylesheet"
@@ -30,7 +30,7 @@
30
30
  <body>
31
31
  <div class="wrapper">
32
32
  <div class="center container">
33
- <h1>Error Authorizing</h1>
33
+ <h1>Authorization Error</h1>
34
34
  <div class="alert alert-danger" role="alert">
35
35
  <% if (typeof error === 'string') { %><%= error %><<% } else { %>
36
36
  <pre><%= JSON.stringify(error,null,2) %></pre>
@@ -1,79 +0,0 @@
1
- import * as Configuration from '@battis/oauth2-configure';
2
- import { Mutex } from 'async-mutex';
3
- import * as client from 'openid-client';
4
- import { FileStorage } from './FileStorage.js';
5
- import * as Localhost from './Localhost.js';
6
- import { Token } from './Token.js';
7
- export class TokenManager {
8
- options;
9
- tokenMutex = new Mutex();
10
- store;
11
- constructor(options) {
12
- this.options = options;
13
- if (this.options.store) {
14
- if (typeof this.options.store === 'string') {
15
- this.store = new FileStorage(this.options.store);
16
- }
17
- else {
18
- this.store = this.options.store;
19
- }
20
- }
21
- }
22
- async getToken() {
23
- return await this.tokenMutex.runExclusive((async () => {
24
- let token = await this.store?.load();
25
- if (token?.hasExpired()) {
26
- token = await this.refreshToken(token);
27
- }
28
- return token || (await this.authorize());
29
- }).bind(this));
30
- }
31
- async refreshToken(token) {
32
- if (token.refresh_token) {
33
- const { headers, parameters } = this.options;
34
- let freshTokens;
35
- if ((freshTokens = Token.fromResponse(await client.refreshTokenGrant(await Configuration.acquire(this.options), token.refresh_token, parameters,
36
- // @ts-ignore FIXME undocumented arg pass-through to oauth4webapi
37
- { headers })))) {
38
- return this.store?.save(freshTokens) || freshTokens;
39
- }
40
- }
41
- return this.authorize();
42
- }
43
- async authorize() {
44
- const { scope, redirect_uri, parameters: additionalParameters } = this.options;
45
- return new Promise(async (resolve, reject) => {
46
- const configuration = await Configuration.acquire(this.options);
47
- const code_verifier = client.randomPKCECodeVerifier();
48
- const code_challenge = await client.calculatePKCECodeChallenge(code_verifier);
49
- let state = undefined;
50
- const parameters = {
51
- ...additionalParameters,
52
- redirect_uri,
53
- code_challenge,
54
- code_challenge_method: 'S256' // TODO make code challenge method configurable?
55
- };
56
- if (scope) {
57
- parameters.scope = scope;
58
- }
59
- if (!configuration.serverMetadata().supportsPKCE()) {
60
- state = client.randomState();
61
- parameters.state = state;
62
- }
63
- await Localhost.redirectServer({
64
- ...this.options,
65
- authorization_url: client.buildAuthorizationUrl(configuration, parameters).href,
66
- code_verifier,
67
- state,
68
- resolve: (async (response) => {
69
- const token = Token.fromResponse(response);
70
- if (token && this.store) {
71
- await this.store.save(token);
72
- }
73
- resolve(token);
74
- }).bind(this),
75
- reject
76
- });
77
- });
78
- }
79
- }