-
Notifications
You must be signed in to change notification settings - Fork 49
Expand file tree
/
Copy pathWebFlow.ts
More file actions
99 lines (86 loc) · 2.98 KB
/
WebFlow.ts
File metadata and controls
99 lines (86 loc) · 2.98 KB
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
import { IPluginMiddleware } from "@verdaccio/types"
import { Application, Handler, Request } from "express"
import { authorizePath, callbackPath } from "../../constants"
import { logger } from "../../logger"
import { buildStatusPage } from "../../statusPage"
import { AuthCore } from "../plugin/AuthCore"
import { AuthProvider } from "../plugin/AuthProvider"
import { Verdaccio } from "../verdaccio"
export const errorPage = buildStatusPage(`
<h1>Access Denied</h1>
<p>You are not a member of the required org.</p>
<p><a href="/">Go back</a></p>
`)
export class WebFlow implements IPluginMiddleware<any> {
static getAuthorizePath(id?: string) {
return authorizePath + "/" + (id || ":id?")
}
static getCallbackPath(id?: string) {
return callbackPath + (id ? "/" + id : "")
}
constructor(
private readonly verdaccio: Verdaccio,
private readonly core: AuthCore,
private readonly provider: AuthProvider,
) { }
/**
* IPluginMiddleware
*/
register_middlewares(app: Application) {
app.get(WebFlow.getAuthorizePath(), this.authorize)
app.get(WebFlow.getCallbackPath(), this.callback)
}
/**
* Initiates the auth flow by redirecting to the provider's login URL.
*/
authorize: Handler = async (req, res, next) => {
try {
const redirectUrl = this.getRedirectUrl(req)
const url = await this.provider.getLoginUrl(redirectUrl)
res.redirect(url)
} catch (error) {
logger.error(error)
next(error)
}
}
/**
* After successful authentication, the auth provider redirects back to us.
* We use the code in the query params to get an access token and the username
* associated with the account.
*
* We issue a JWT using these values and pass them back to the frontend as
* query parameters so they can be stored in the browser.
*
* The username and token are encrypted and base64 encoded to form a token for
* the npm CLI.
*
* There is no need to later decode and decrypt the token. This process is
* automatically reversed by verdaccio before passing it to the plugin.
*/
callback: Handler = async (req, res, next) => {
try {
const code = await this.provider.getCode(req)
const token = await this.provider.getToken(code)
const username = await this.provider.getUsername(token)
const groups = await this.provider.getGroups(token)
if (this.core.canAuthenticate(username, groups)) {
const ui = await this.core.createUiCallbackUrl(username, token)
res.redirect(ui)
} else {
res.send(errorPage)
}
} catch (error) {
logger.error(error)
next(error)
}
}
private getRedirectUrl(req: Request): string {
const baseUrl = this.verdaccio.baseUrl || this.getRequestOrigin(req)
const path = WebFlow.getCallbackPath(req.params.id)
return baseUrl + path
}
private getRequestOrigin(req: Request) {
const protocal = req.get("X-Forwarded-Proto") || req.protocol
return protocal + "://" + req.get("host")
}
}