forked from pnp/cli-microsoft365
-
Notifications
You must be signed in to change notification settings - Fork 0
/
AuthServer.ts
130 lines (109 loc) · 5.23 KB
/
AuthServer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import http, { IncomingMessage, ServerResponse } from 'http';
import { AddressInfo } from 'net';
import { ParsedUrlQuery } from 'querystring';
import url from 'url';
import { Auth, InteractiveAuthorizationCodeResponse, InteractiveAuthorizationErrorResponse, Service } from './Auth.js';
import { Logger } from './cli/Logger.js';
import { browserUtil } from './utils/browserUtil.js';
export class AuthServer {
// assigned through this.initializeServer() hence !
private httpServer!: http.Server;
// assigned through this.initializeServer() hence !
private service!: Service;
// assigned through this.initializeServer() hence !
private resolve!: (error: InteractiveAuthorizationCodeResponse) => void;
// assigned through this.initializeServer() hence !
private reject!: (error: InteractiveAuthorizationErrorResponse) => void;
// assigned through this.initializeServer() hence !
private logger!: Logger;
private debug: boolean = false;
private resource: string = "";
private generatedServerUrl: string = "";
public get server(): http.Server {
return this.httpServer;
}
public initializeServer = (service: Service, resource: string, resolve: (result: InteractiveAuthorizationCodeResponse) => void, reject: (error: InteractiveAuthorizationErrorResponse) => void, logger: Logger, debug: boolean = false): void => {
this.service = service;
this.resolve = resolve;
this.reject = reject;
this.logger = logger;
this.debug = debug;
this.resource = resource;
this.httpServer = http.createServer(this.httpRequest).listen(0, this.httpListener);
};
private httpListener = (): void => {
const requestState = Math.random().toString(16).substr(2, 20);
const address = this.httpServer.address() as AddressInfo;
this.generatedServerUrl = `http://localhost:${address.port}`;
const url = `${Auth.getEndpointForResource('https://login.microsoftonline.com', this.service.cloudType)}/${this.service.tenant}/oauth2/authorize?response_type=code&client_id=${this.service.appId}&redirect_uri=${this.generatedServerUrl}&state=${requestState}&resource=${this.resource}&prompt=select_account`;
if (this.debug) {
this.logger.logToStderr('Redirect URL:');
this.logger.logToStderr(url);
this.logger.logToStderr('');
}
this.openUrl(url);
};
private openUrl(url: string): void {
browserUtil.open(url)
.then(_ => {
this.logger.logToStderr("To sign in, use the web browser that just has been opened. Please sign-in there.");
})
.catch(_ => {
const errorResponse: InteractiveAuthorizationErrorResponse = {
error: "Can't open the default browser",
errorDescription: "Was not able to open a browser instance. Try again later or use a different authentication method."
};
this.reject(errorResponse);
this.httpServer.close();
});
}
private httpRequest = (request: IncomingMessage, response: ServerResponse): void => {
if (this.debug) {
this.logger.logToStderr('Response:');
this.logger.logToStderr(request.url);
this.logger.logToStderr('');
}
// url.parse is deprecated but we can't move to URL, because it doesn't
// support server-relative URLs
const queryString: ParsedUrlQuery = url.parse(request.url as string, true).query;
const hasCode: boolean = queryString.code !== undefined;
const hasError: boolean = queryString.error !== undefined;
let body: string = "";
if (hasCode === true) {
body = '<script type="text/JavaScript">setTimeout(function(){ window.location = "https://pnp.github.io/cli-microsoft365/"; },10000);</script>';
body += '<p><b>You have logged into CLI for Microsoft 365!</b></p>';
body += '<p>You can close this window, or we will redirect you to the <a href="https://pnp.github.io/cli-microsoft365/">CLI for Microsoft 365</a> documentation in 10 seconds.</p>';
this.resolve(<InteractiveAuthorizationCodeResponse>{
code: queryString.code as string,
redirectUri: this.generatedServerUrl
});
}
if (hasError === true) {
const errorMessage: InteractiveAuthorizationErrorResponse = {
error: queryString.error as string,
errorDescription: queryString.error_description as string
};
body = "<p>Oops! Azure Active Directory replied with an error message.</p>";
body += `<p>${errorMessage.error}</p>`;
if (errorMessage.errorDescription !== undefined) {
body += `<p>${errorMessage.errorDescription}</p>`;
}
this.reject(errorMessage);
}
if (hasCode === false && hasError === false) {
const errorMessage: InteractiveAuthorizationErrorResponse = {
error: "invalid request",
errorDescription: "An invalid request has been received by the HTTP server"
};
body = "<p>Oops! This is an invalid request.</p>";
body += `<p>${errorMessage.error}</p>`;
body += `<p>${errorMessage.errorDescription}</p>`;
this.reject(errorMessage);
}
response.writeHead(200, { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'text/html' });
response.write(`<html><head><title>CLI for Microsoft 365</title></head><body>${body}</body></html>`);
response.end();
this.httpServer.close();
};
}
export default new AuthServer();