oauth2-cli 0.8.1 → 0.8.2
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 +12 -0
- package/dist/Client.d.ts +1 -0
- package/dist/Client.js +19 -5
- package/dist/Session.d.ts +7 -5
- package/dist/Session.js +48 -24
- package/dist/WebServer.d.ts +3 -1
- package/dist/WebServer.js +7 -7
- package/package.json +1 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
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.8.2](https://github.com/battis/oauth2-cli/compare/oauth2-cli/0.8.1...oauth2-cli/0.8.2) (2026-02-18)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* isAuthorized() ([56b0436](https://github.com/battis/oauth2-cli/commit/56b0436d98b9d9f213518e806ead846321180a63))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* block thread until interactive authorization complete ([fb9987e](https://github.com/battis/oauth2-cli/commit/fb9987e41a5a8f129955bc3e88ab17994860091b)), closes [#22](https://github.com/battis/oauth2-cli/issues/22)
|
|
16
|
+
|
|
5
17
|
## [0.8.1](https://github.com/battis/oauth2-cli/compare/oauth2-cli/0.8.0...oauth2-cli/0.8.1) (2026-02-18)
|
|
6
18
|
|
|
7
19
|
|
package/dist/Client.d.ts
CHANGED
|
@@ -84,6 +84,7 @@ export declare class Client<C extends Credentials = Credentials> extends EventEm
|
|
|
84
84
|
protected getParameters(session: Session): Promise<URLSearchParams>;
|
|
85
85
|
getAuthorizationUrl(session: Session): Promise<URL>;
|
|
86
86
|
createSession({ views, ...options }: Omit<SessionOptions, 'client'>): Session;
|
|
87
|
+
isAuthorized(): Promise<boolean>;
|
|
87
88
|
authorize(options?: Omit<SessionOptions, 'client'>): Promise<Token.Response>;
|
|
88
89
|
handleAuthorizationCodeRedirect(req: Request, session: Session): Promise<void>;
|
|
89
90
|
protected refreshTokenGrant({ refresh_token, inject: request }?: RefreshOptions): Promise<Token.Response | undefined>;
|
package/dist/Client.js
CHANGED
|
@@ -91,6 +91,16 @@ export class Client extends EventEmitter {
|
|
|
91
91
|
...options
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
|
+
async isAuthorized() {
|
|
95
|
+
if (this.token?.expiresIn()) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
return await this.tokenLock.runExclusive(async () => {
|
|
100
|
+
return !!(await this.refreshTokenGrant());
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
94
104
|
async authorize(options = {}) {
|
|
95
105
|
const session = this.createSession(options);
|
|
96
106
|
const token = await this.save(await session.authorizationCodeGrant());
|
|
@@ -98,16 +108,20 @@ export class Client extends EventEmitter {
|
|
|
98
108
|
}
|
|
99
109
|
async handleAuthorizationCodeRedirect(req, session) {
|
|
100
110
|
try {
|
|
101
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Do _NOT_ await this promise: the WebServer needs to send the
|
|
113
|
+
* authorization complete response asynchronously before this can resolve,
|
|
114
|
+
* and awaiting session.resolve() will block that response.
|
|
115
|
+
*/
|
|
116
|
+
session.resolve(await OpenIDClient.authorizationCodeGrant(await this.getConfiguration(), new URL(req.url, this.redirect_uri), {
|
|
102
117
|
pkceCodeVerifier: session.code_verifier,
|
|
103
118
|
expectedState: session.state
|
|
104
119
|
}, this.inject?.search
|
|
105
120
|
? requestish.URLSearchParams.from(this.inject.search)
|
|
106
|
-
: undefined);
|
|
107
|
-
await session.resolve(response);
|
|
121
|
+
: undefined));
|
|
108
122
|
}
|
|
109
|
-
catch (
|
|
110
|
-
|
|
123
|
+
catch (cause) {
|
|
124
|
+
session.reject(new Error('Error making Authorization Code Grant request', { cause }));
|
|
111
125
|
}
|
|
112
126
|
}
|
|
113
127
|
async refreshTokenGrant({ refresh_token = this.token?.refresh_token, inject: request } = {}) {
|
package/dist/Session.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type SessionOptions = {
|
|
|
11
11
|
/** Additional request injection for authorization code grant flow */
|
|
12
12
|
inject?: Injection;
|
|
13
13
|
};
|
|
14
|
-
export type
|
|
14
|
+
export type SessionResolver = (response: Token.Response) => void | Promise<void>;
|
|
15
15
|
export declare class Session {
|
|
16
16
|
private readonly client;
|
|
17
17
|
private readonly outOfBandRedirectServer;
|
|
@@ -21,19 +21,21 @@ export declare class Session {
|
|
|
21
21
|
readonly state: string;
|
|
22
22
|
/** Additional request injection for Authorization Code Grant request */
|
|
23
23
|
readonly inject?: Injection;
|
|
24
|
+
private spinner;
|
|
24
25
|
private _resolve?;
|
|
25
|
-
private spinner?;
|
|
26
26
|
/**
|
|
27
27
|
* Method that resolves or rejects the promise returned from the
|
|
28
28
|
* {@link authorizationCodeGrant}
|
|
29
29
|
*/
|
|
30
|
-
get resolve():
|
|
30
|
+
get resolve(): SessionResolver;
|
|
31
|
+
reject(error: Error): void;
|
|
31
32
|
constructor({ client, views, inject: request }: SessionOptions);
|
|
32
33
|
/** Instantiate the web server that will listen for the out-of-band redirect */
|
|
33
|
-
|
|
34
|
+
protected instantiateWebServer(options: Omit<WebServer.WebServerOptions, 'session'>): WebServer.WebServerInterface;
|
|
34
35
|
/**
|
|
35
36
|
* Trigger the start of the Authorization Code Grant flow, returnig a Promise
|
|
36
|
-
* that will resolve into the eventual token
|
|
37
|
+
* that will resolve into the eventual token. This will close the out-of-band
|
|
38
|
+
* redirect server that creating the session started.
|
|
37
39
|
*/
|
|
38
40
|
authorizationCodeGrant(): Promise<Token.Response>;
|
|
39
41
|
/** OAuth 2.0 redirect_uri that this session is handling */
|
package/dist/Session.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Colors } from '@qui-cli/colors';
|
|
2
2
|
import * as gcrtl from 'gcrtl';
|
|
3
|
-
import open from 'open';
|
|
4
3
|
import * as OpenIDClient from 'openid-client';
|
|
5
4
|
import ora from 'ora';
|
|
6
5
|
import * as WebServer from './WebServer.js';
|
|
@@ -13,8 +12,8 @@ export class Session {
|
|
|
13
12
|
state = OpenIDClient.randomState();
|
|
14
13
|
/** Additional request injection for Authorization Code Grant request */
|
|
15
14
|
inject;
|
|
16
|
-
_resolve;
|
|
17
15
|
spinner;
|
|
16
|
+
_resolve;
|
|
18
17
|
/**
|
|
19
18
|
* Method that resolves or rejects the promise returned from the
|
|
20
19
|
* {@link authorizationCodeGrant}
|
|
@@ -25,37 +24,61 @@ export class Session {
|
|
|
25
24
|
}
|
|
26
25
|
return this._resolve;
|
|
27
26
|
}
|
|
27
|
+
reject(error) {
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
28
30
|
constructor({ client, views, inject: request }) {
|
|
31
|
+
this.spinner = ora('Awaiting interactive authorization').start();
|
|
29
32
|
this.client = client;
|
|
30
33
|
this.inject = request;
|
|
31
|
-
this.outOfBandRedirectServer = this.
|
|
34
|
+
this.outOfBandRedirectServer = this.instantiateWebServer({ views });
|
|
32
35
|
}
|
|
33
36
|
/** Instantiate the web server that will listen for the out-of-band redirect */
|
|
34
|
-
|
|
37
|
+
instantiateWebServer(options) {
|
|
35
38
|
return new WebServer.WebServer({ session: this, ...options });
|
|
36
39
|
}
|
|
37
40
|
/**
|
|
38
41
|
* Trigger the start of the Authorization Code Grant flow, returnig a Promise
|
|
39
|
-
* that will resolve into the eventual token
|
|
42
|
+
* that will resolve into the eventual token. This will close the out-of-band
|
|
43
|
+
* redirect server that creating the session started.
|
|
40
44
|
*/
|
|
41
|
-
authorizationCodeGrant() {
|
|
42
|
-
return new Promise((resolve, reject) => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
45
|
+
async authorizationCodeGrant() {
|
|
46
|
+
return await new Promise((resolve, reject) => {
|
|
47
|
+
try {
|
|
48
|
+
this._resolve = (response) => {
|
|
49
|
+
let closed = false;
|
|
50
|
+
this.spinner.text =
|
|
51
|
+
'Waiting for out-of-band redirect server to shut down';
|
|
52
|
+
this.outOfBandRedirectServer.close().then(() => {
|
|
53
|
+
closed = true;
|
|
54
|
+
this.spinner.succeed('Interactive authorization complete');
|
|
55
|
+
resolve(response);
|
|
56
|
+
});
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
if (!closed) {
|
|
59
|
+
this.spinner.text =
|
|
60
|
+
'Still waiting for out-of-band redirect server to shut down.\n' +
|
|
61
|
+
' Your browser may be holding the connection to the server open.\n' +
|
|
62
|
+
' Please close the "Authorization Complete" tab in your browser.\n\n' +
|
|
63
|
+
' If you are browsing in Chrome, close the window.\n' +
|
|
64
|
+
' If you are browsing in Opera, quit the browser.';
|
|
65
|
+
}
|
|
66
|
+
}, 10000);
|
|
67
|
+
};
|
|
68
|
+
const url = gcrtl
|
|
69
|
+
.expand(this.outOfBandRedirectServer.authorization_endpoint, this.client.redirect_uri)
|
|
70
|
+
.toString();
|
|
71
|
+
//open(url);
|
|
72
|
+
this.spinner.text = `Please continue interactive authorization at ${Colors.url(url)} in your browser`;
|
|
73
|
+
}
|
|
74
|
+
catch (cause) {
|
|
75
|
+
this.spinner.text =
|
|
76
|
+
'Waiting for out-of-band redirect server to shut down';
|
|
77
|
+
this.outOfBandRedirectServer.close().then(() => {
|
|
78
|
+
this.spinner.fail('Interactive authorization failed');
|
|
79
|
+
reject(new Error('Error in Authorization Code flow', { cause }));
|
|
80
|
+
});
|
|
81
|
+
}
|
|
59
82
|
});
|
|
60
83
|
}
|
|
61
84
|
/** OAuth 2.0 redirect_uri that this session is handling */
|
|
@@ -70,7 +93,8 @@ export class Session {
|
|
|
70
93
|
* Code Grant flow
|
|
71
94
|
*/
|
|
72
95
|
async handleAuthorizationCodeRedirect(req) {
|
|
73
|
-
this.spinner
|
|
96
|
+
this.spinner.text =
|
|
97
|
+
'Completing access token request with provided authorization code';
|
|
74
98
|
return await this.client.handleAuthorizationCodeRedirect(req, this);
|
|
75
99
|
}
|
|
76
100
|
}
|
package/dist/WebServer.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export declare const DEFAULT_AUTHORIZE_ENDPOINT = "/oauth2-cli/authorize";
|
|
|
20
20
|
export interface WebServerInterface {
|
|
21
21
|
/** See {@link WebServerOptions} */
|
|
22
22
|
readonly authorization_endpoint: PathString;
|
|
23
|
+
/** Shut down web server */
|
|
24
|
+
close(): Promise<void>;
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
27
|
* Minimal HTTP server running on localhost to handle the redirect step of
|
|
@@ -59,6 +61,6 @@ export declare class WebServer implements WebServerInterface {
|
|
|
59
61
|
protected handleAuthorizationEndpoint(req: Request, res: Response): Promise<void>;
|
|
60
62
|
/** Handles request to `redirect_uri` */
|
|
61
63
|
protected handleRedirect(req: Request, res: Response): Promise<void>;
|
|
62
|
-
/**
|
|
64
|
+
/** Shut down web server */
|
|
63
65
|
close(): Promise<void>;
|
|
64
66
|
}
|
package/dist/WebServer.js
CHANGED
|
@@ -79,6 +79,7 @@ export class WebServer {
|
|
|
79
79
|
const authorization_url = await this.session.getAuthorizationUrl();
|
|
80
80
|
if (!(await this.render(res, 'authorize.ejs', { authorization_url }))) {
|
|
81
81
|
res.redirect(authorization_url);
|
|
82
|
+
res.end();
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
85
|
/** Handles request to `redirect_uri` */
|
|
@@ -94,16 +95,15 @@ export class WebServer {
|
|
|
94
95
|
res.send(error);
|
|
95
96
|
}
|
|
96
97
|
}
|
|
97
|
-
finally {
|
|
98
|
-
this.close();
|
|
99
|
-
}
|
|
100
98
|
}
|
|
101
|
-
/**
|
|
99
|
+
/** Shut down web server */
|
|
102
100
|
async close() {
|
|
103
101
|
return new Promise((resolve, reject) => {
|
|
104
|
-
this.server.close((
|
|
105
|
-
if (
|
|
106
|
-
reject(
|
|
102
|
+
this.server.close((cause) => {
|
|
103
|
+
if (cause) {
|
|
104
|
+
reject(new Error('Error shutting down out-of-band redirect web server', {
|
|
105
|
+
cause
|
|
106
|
+
}));
|
|
107
107
|
}
|
|
108
108
|
else {
|
|
109
109
|
WebServer.activePorts.splice(WebServer.activePorts.indexOf(this.port), 1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oauth2-cli",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
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,7 +20,6 @@
|
|
|
20
20
|
"async-mutex": "^0.5.0",
|
|
21
21
|
"express": "^5.2.1",
|
|
22
22
|
"oauth4webapi": "^3.8.5",
|
|
23
|
-
"open": "^11.0.0",
|
|
24
23
|
"openid-client": "^6.8.2",
|
|
25
24
|
"ora": "^9.3.0",
|
|
26
25
|
"gcrtl": "0.1.7",
|