1 |
|
2 |
|
3 | const debug = require('./../debug').authentication
|
4 |
|
5 | const AuthRequest = require('./auth-request')
|
6 | const { PasswordAuthenticator, TlsAuthenticator } = require('../models/authenticator')
|
7 |
|
8 | const PASSWORD_AUTH = 'password'
|
9 | const TLS_AUTH = 'tls'
|
10 |
|
11 | /**
|
12 | * Models a local Login request
|
13 | */
|
14 | class LoginRequest extends AuthRequest {
|
15 | /**
|
16 | * @constructor
|
17 | * @param options {Object}
|
18 | *
|
19 | * @param [options.response] {ServerResponse} middleware `res` object
|
20 | * @param [options.session] {Session} req.session
|
21 | * @param [options.userStore] {UserStore}
|
22 | * @param [options.accountManager] {AccountManager}
|
23 | * @param [options.returnToUrl] {string}
|
24 | * @param [options.authQueryParams] {Object} Key/value hashmap of parsed query
|
25 | * parameters that will be passed through to the /authorize endpoint.
|
26 | * @param [options.authenticator] {Authenticator} Auth strategy by which to
|
27 | * log in
|
28 | */
|
29 | constructor (options) {
|
30 | super(options)
|
31 |
|
32 | this.authenticator = options.authenticator
|
33 | this.authMethod = options.authMethod
|
34 | }
|
35 |
|
36 | /**
|
37 | * Factory method, returns an initialized instance of LoginRequest
|
38 | * from an incoming http request.
|
39 | *
|
40 | * @param req {IncomingRequest}
|
41 | * @param res {ServerResponse}
|
42 | * @param authMethod {string}
|
43 | *
|
44 | * @return {LoginRequest}
|
45 | */
|
46 | static fromParams (req, res, authMethod) {
|
47 | let options = AuthRequest.requestOptions(req, res)
|
48 | options.authMethod = authMethod
|
49 |
|
50 | switch (authMethod) {
|
51 | case PASSWORD_AUTH:
|
52 | options.authenticator = PasswordAuthenticator.fromParams(req, options)
|
53 | break
|
54 |
|
55 | case TLS_AUTH:
|
56 | options.authenticator = TlsAuthenticator.fromParams(req, options)
|
57 | break
|
58 |
|
59 | default:
|
60 | options.authenticator = null
|
61 | break
|
62 | }
|
63 |
|
64 | return new LoginRequest(options)
|
65 | }
|
66 |
|
67 | /**
|
68 | * Handles a Login GET request on behalf of a middleware handler, displays
|
69 | * the Login page.
|
70 | * Usage:
|
71 | *
|
72 | * ```
|
73 | * app.get('/login', LoginRequest.get)
|
74 | * ```
|
75 | *
|
76 | * @param req {IncomingRequest}
|
77 | * @param res {ServerResponse}
|
78 | */
|
79 | static get (req, res) {
|
80 | const request = LoginRequest.fromParams(req, res)
|
81 |
|
82 | request.renderForm(null, req)
|
83 | }
|
84 |
|
85 | /**
|
86 | * Handles a Login via Username+Password.
|
87 | * Errors encountered are displayed on the Login form.
|
88 | * Usage:
|
89 | *
|
90 | * ```
|
91 | * app.post('/login/password', LoginRequest.loginPassword)
|
92 | * ```
|
93 | *
|
94 | * @param req
|
95 | * @param res
|
96 | *
|
97 | * @return {Promise}
|
98 | */
|
99 | static loginPassword (req, res) {
|
100 | debug('Logging in via username + password')
|
101 |
|
102 | let request = LoginRequest.fromParams(req, res, PASSWORD_AUTH)
|
103 |
|
104 | return LoginRequest.login(request)
|
105 | }
|
106 |
|
107 | /**
|
108 | * Handles a Login via WebID-TLS.
|
109 | * Errors encountered are displayed on the Login form.
|
110 | * Usage:
|
111 | *
|
112 | * ```
|
113 | * app.post('/login/tls', LoginRequest.loginTls)
|
114 | * ```
|
115 | *
|
116 | * @param req
|
117 | * @param res
|
118 | *
|
119 | * @return {Promise}
|
120 | */
|
121 | static loginTls (req, res) {
|
122 | debug('Logging in via WebID-TLS certificate')
|
123 |
|
124 | let request = LoginRequest.fromParams(req, res, TLS_AUTH)
|
125 |
|
126 | return LoginRequest.login(request)
|
127 | }
|
128 |
|
129 | /**
|
130 | * Performs the login operation -- loads and validates the
|
131 | * appropriate user, inits the session with credentials, and redirects the
|
132 | * user to continue their auth flow.
|
133 | *
|
134 | * @param request {LoginRequest}
|
135 | *
|
136 | * @return {Promise}
|
137 | */
|
138 | static login (request) {
|
139 | return request.authenticator.findValidUser()
|
140 |
|
141 | .then(validUser => {
|
142 | request.initUserSession(validUser)
|
143 |
|
144 | request.redirectPostLogin(validUser)
|
145 | })
|
146 |
|
147 | .catch(error => request.error(error))
|
148 | }
|
149 |
|
150 | /**
|
151 | * Returns a URL to redirect the user to after login.
|
152 | * Either uses the provided `redirect_uri` auth query param, or simply
|
153 | * returns the user profile URI if none was provided.
|
154 | *
|
155 | * @param validUser {UserAccount}
|
156 | *
|
157 | * @return {string}
|
158 | */
|
159 | postLoginUrl (validUser) {
|
160 | // Login request is part of an app's auth flow
|
161 | if (/token|code/.test(this.authQueryParams['response_type'])) {
|
162 | return this.sharingUrl()
|
163 | // Login request is a user going to /login in browser
|
164 | } else if (validUser) {
|
165 | return this.authQueryParams['redirect_uri'] || validUser.accountUri
|
166 | }
|
167 | }
|
168 |
|
169 | /**
|
170 | * Redirects the Login request to continue on the OIDC auth workflow.
|
171 | */
|
172 | redirectPostLogin (validUser) {
|
173 | let uri = this.postLoginUrl(validUser)
|
174 | debug('Login successful, redirecting to ', uri)
|
175 | this.response.redirect(uri)
|
176 | }
|
177 |
|
178 | /**
|
179 | * Renders the login form
|
180 | */
|
181 | renderForm (error, req) {
|
182 | let queryString = req && req.url && req.url.replace(/[^?]+\?/, '') || ''
|
183 | let params = Object.assign({}, this.authQueryParams,
|
184 | {
|
185 | registerUrl: this.registerUrl(),
|
186 | returnToUrl: this.returnToUrl,
|
187 | enablePassword: this.localAuth.password,
|
188 | enableTls: this.localAuth.tls,
|
189 | tlsUrl: `/login/tls?${encodeURIComponent(queryString)}`
|
190 | })
|
191 |
|
192 | if (error) {
|
193 | params.error = error.message
|
194 | this.response.status(error.statusCode)
|
195 | }
|
196 | this.response.render('auth/login', params)
|
197 | }
|
198 | }
|
199 |
|
200 | module.exports = {
|
201 | LoginRequest,
|
202 | PASSWORD_AUTH,
|
203 | TLS_AUTH
|
204 | }
|