@adonisjs/auth 9.0.0-0 → 9.0.0-10
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/build/configure.js +5 -26
- package/build/factories/basic_auth_guard_factory.d.ts +12 -0
- package/build/factories/basic_auth_guard_factory.js +22 -0
- package/build/index.d.ts +1 -2
- package/build/index.js +1 -2
- package/build/src/auth/auth_manager.d.ts +9 -0
- package/build/src/auth/auth_manager.js +13 -0
- package/build/src/auth/authenticator.d.ts +45 -0
- package/build/src/auth/authenticator.js +74 -0
- package/build/src/auth/authenticator_client.d.ts +23 -0
- package/build/src/auth/authenticator_client.js +59 -0
- package/build/src/auth/errors.d.ts +86 -4
- package/build/src/auth/errors.js +189 -5
- package/build/src/auth/middleware/initialize_auth_middleware.d.ts +18 -0
- package/build/src/auth/middleware/initialize_auth_middleware.js +25 -0
- package/build/src/auth/plugins/japa/api_client.d.ts +32 -0
- package/build/src/auth/plugins/japa/api_client.js +63 -0
- package/build/src/auth/plugins/japa/browser_client.d.ts +25 -0
- package/build/src/auth/plugins/japa/browser_client.js +64 -0
- package/build/src/auth/types.d.ts +60 -8
- package/build/src/core/token.d.ts +4 -1
- package/build/src/core/token.js +5 -3
- package/build/src/core/token_providers/database.d.ts +1 -1
- package/build/src/guards/basic_auth/define_config.d.ts +16 -0
- package/build/src/guards/basic_auth/define_config.js +38 -0
- package/build/src/guards/basic_auth/guard.d.ts +70 -0
- package/build/src/guards/basic_auth/guard.js +193 -0
- package/build/src/guards/basic_auth/main.d.ts +2 -0
- package/build/{stubs → src/guards/basic_auth}/main.js +2 -2
- package/build/src/guards/basic_auth/types.d.ts +40 -0
- package/build/src/guards/basic_auth/types.js +9 -0
- package/build/src/guards/session/define_config.js +1 -1
- package/build/src/guards/session/guard.d.ts +33 -2
- package/build/src/guards/session/guard.js +153 -11
- package/build/src/guards/session/types.d.ts +19 -3
- package/package.json +54 -30
- package/build/stubs/config/auth_middleware.stub +0 -12
- package/build/stubs/config.stub +0 -35
- package/build/stubs/main.d.ts +0 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
2
|
+
import type { EmitterLike } from '@adonisjs/core/types/events';
|
|
3
|
+
import type { BasicAuthGuardEvents } from './types.js';
|
|
4
|
+
import type { GuardContract } from '../../auth/types.js';
|
|
5
|
+
import type { UserProviderContract } from '../../core/types.js';
|
|
6
|
+
import { PROVIDER_REAL_USER, GUARD_KNOWN_EVENTS } from '../../auth/symbols.js';
|
|
7
|
+
/**
|
|
8
|
+
* Implementation of basic auth as an authentication guard
|
|
9
|
+
*/
|
|
10
|
+
export declare class BasicAuthGuard<UserProvider extends UserProviderContract<unknown>> implements GuardContract<UserProvider[typeof PROVIDER_REAL_USER]> {
|
|
11
|
+
#private;
|
|
12
|
+
[GUARD_KNOWN_EVENTS]: BasicAuthGuardEvents<UserProvider[typeof PROVIDER_REAL_USER]>;
|
|
13
|
+
/**
|
|
14
|
+
* Driver name of the guard
|
|
15
|
+
*/
|
|
16
|
+
driverName: 'basic_auth';
|
|
17
|
+
/**
|
|
18
|
+
* Whether or not the authentication has been attempted
|
|
19
|
+
* during the current request
|
|
20
|
+
*/
|
|
21
|
+
authenticationAttempted: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* A boolean to know if the current request has
|
|
24
|
+
* been authenticated
|
|
25
|
+
*/
|
|
26
|
+
isAuthenticated: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Reference to an instance of the authenticated or logged-in
|
|
29
|
+
* user. The value only exists after calling one of the
|
|
30
|
+
* following methods.
|
|
31
|
+
*
|
|
32
|
+
* - authenticate
|
|
33
|
+
*
|
|
34
|
+
* You can use the "getUserOrFail" method to throw an exception if
|
|
35
|
+
* the request is not authenticated.
|
|
36
|
+
*/
|
|
37
|
+
user?: UserProvider[typeof PROVIDER_REAL_USER];
|
|
38
|
+
constructor(name: string, ctx: HttpContext, userProvider: UserProvider);
|
|
39
|
+
/**
|
|
40
|
+
* Register an event emitter to listen for global events for
|
|
41
|
+
* authentication lifecycle.
|
|
42
|
+
*/
|
|
43
|
+
setEmitter(emitter: EmitterLike<any>): this;
|
|
44
|
+
/**
|
|
45
|
+
* Returns an instance of the authenticated user. Or throws
|
|
46
|
+
* an exception if the request is not authenticated.
|
|
47
|
+
*/
|
|
48
|
+
getUserOrFail(): UserProvider[typeof PROVIDER_REAL_USER];
|
|
49
|
+
/**
|
|
50
|
+
* Verifies user credentials and returns an instance of
|
|
51
|
+
* the user or throws "E_INVALID_BASIC_AUTH_CREDENTIALS" exception.
|
|
52
|
+
*/
|
|
53
|
+
verifyCredentials(uid: string, password: string): Promise<UserProvider[typeof PROVIDER_REAL_USER]>;
|
|
54
|
+
/**
|
|
55
|
+
* Authenticates the current HTTP request for basic
|
|
56
|
+
* auth credentials
|
|
57
|
+
*/
|
|
58
|
+
authenticate(): Promise<UserProvider[typeof PROVIDER_REAL_USER]>;
|
|
59
|
+
/**
|
|
60
|
+
* Silently attempt to authenticate the user.
|
|
61
|
+
*
|
|
62
|
+
* The method returns a boolean indicating if the authentication
|
|
63
|
+
* succeeded or failed.
|
|
64
|
+
*/
|
|
65
|
+
check(): Promise<boolean>;
|
|
66
|
+
/**
|
|
67
|
+
* Not support
|
|
68
|
+
*/
|
|
69
|
+
authenticateAsClient(_: UserProvider[typeof PROVIDER_REAL_USER]): Promise<never>;
|
|
70
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @adonisjs/auth
|
|
3
|
+
*
|
|
4
|
+
* (c) AdonisJS
|
|
5
|
+
*
|
|
6
|
+
* For the full copyright and license information, please view the LICENSE
|
|
7
|
+
* file that was distributed with this source code.
|
|
8
|
+
*/
|
|
9
|
+
import auth from 'basic-auth';
|
|
10
|
+
import { RuntimeException } from '@poppinss/utils';
|
|
11
|
+
import debug from '../../auth/debug.js';
|
|
12
|
+
import { AuthenticationException } from '../../auth/errors.js';
|
|
13
|
+
import { GUARD_KNOWN_EVENTS } from '../../auth/symbols.js';
|
|
14
|
+
/**
|
|
15
|
+
* Implementation of basic auth as an authentication guard
|
|
16
|
+
*/
|
|
17
|
+
export class BasicAuthGuard {
|
|
18
|
+
/**
|
|
19
|
+
* A unique name for the guard. It is used while
|
|
20
|
+
* emitting events
|
|
21
|
+
*/
|
|
22
|
+
#name;
|
|
23
|
+
/**
|
|
24
|
+
* Reference to the current HTTP context
|
|
25
|
+
*/
|
|
26
|
+
#ctx;
|
|
27
|
+
/**
|
|
28
|
+
* Provider to lookup user details
|
|
29
|
+
*/
|
|
30
|
+
#userProvider;
|
|
31
|
+
/**
|
|
32
|
+
* Emitter to emit events
|
|
33
|
+
*/
|
|
34
|
+
#emitter;
|
|
35
|
+
/**
|
|
36
|
+
* Driver name of the guard
|
|
37
|
+
*/
|
|
38
|
+
driverName = 'basic_auth';
|
|
39
|
+
/**
|
|
40
|
+
* Whether or not the authentication has been attempted
|
|
41
|
+
* during the current request
|
|
42
|
+
*/
|
|
43
|
+
authenticationAttempted = false;
|
|
44
|
+
/**
|
|
45
|
+
* A boolean to know if the current request has
|
|
46
|
+
* been authenticated
|
|
47
|
+
*/
|
|
48
|
+
isAuthenticated = false;
|
|
49
|
+
/**
|
|
50
|
+
* Reference to an instance of the authenticated or logged-in
|
|
51
|
+
* user. The value only exists after calling one of the
|
|
52
|
+
* following methods.
|
|
53
|
+
*
|
|
54
|
+
* - authenticate
|
|
55
|
+
*
|
|
56
|
+
* You can use the "getUserOrFail" method to throw an exception if
|
|
57
|
+
* the request is not authenticated.
|
|
58
|
+
*/
|
|
59
|
+
user;
|
|
60
|
+
constructor(name, ctx, userProvider) {
|
|
61
|
+
this.#ctx = ctx;
|
|
62
|
+
this.#name = name;
|
|
63
|
+
this.#userProvider = userProvider;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Notifies about authentication failure and throws the exception
|
|
67
|
+
*/
|
|
68
|
+
#authenticationFailed(error) {
|
|
69
|
+
if (this.#emitter) {
|
|
70
|
+
this.#emitter.emit('basic_auth:authentication_failed', {
|
|
71
|
+
ctx: this.#ctx,
|
|
72
|
+
guardName: this.#name,
|
|
73
|
+
error,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Register an event emitter to listen for global events for
|
|
80
|
+
* authentication lifecycle.
|
|
81
|
+
*/
|
|
82
|
+
setEmitter(emitter) {
|
|
83
|
+
this.#emitter = emitter;
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Returns an instance of the authenticated user. Or throws
|
|
88
|
+
* an exception if the request is not authenticated.
|
|
89
|
+
*/
|
|
90
|
+
getUserOrFail() {
|
|
91
|
+
if (!this.user) {
|
|
92
|
+
throw AuthenticationException.E_INVALID_BASIC_AUTH_CREDENTIALS();
|
|
93
|
+
}
|
|
94
|
+
return this.user;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Verifies user credentials and returns an instance of
|
|
98
|
+
* the user or throws "E_INVALID_BASIC_AUTH_CREDENTIALS" exception.
|
|
99
|
+
*/
|
|
100
|
+
async verifyCredentials(uid, password) {
|
|
101
|
+
debug('basic_auth_guard: attempting to verify credentials for uid "%s"', uid);
|
|
102
|
+
/**
|
|
103
|
+
* Attempt to find a user by the uid and raise
|
|
104
|
+
* error when unable to find one
|
|
105
|
+
*/
|
|
106
|
+
const providerUser = await this.#userProvider.findByUid(uid);
|
|
107
|
+
if (!providerUser) {
|
|
108
|
+
this.#authenticationFailed(AuthenticationException.E_INVALID_BASIC_AUTH_CREDENTIALS());
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Raise error when unable to verify password
|
|
112
|
+
*/
|
|
113
|
+
const user = providerUser.getOriginal();
|
|
114
|
+
/**
|
|
115
|
+
* Raise error when unable to verify password
|
|
116
|
+
*/
|
|
117
|
+
if (!(await providerUser.verifyPassword(password))) {
|
|
118
|
+
this.#authenticationFailed(AuthenticationException.E_INVALID_BASIC_AUTH_CREDENTIALS());
|
|
119
|
+
}
|
|
120
|
+
return user;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Authenticates the current HTTP request for basic
|
|
124
|
+
* auth credentials
|
|
125
|
+
*/
|
|
126
|
+
async authenticate() {
|
|
127
|
+
/**
|
|
128
|
+
* Avoid re-authenticating when already authenticated
|
|
129
|
+
*/
|
|
130
|
+
if (this.authenticationAttempted) {
|
|
131
|
+
return this.getUserOrFail();
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Beginning authentication attempt
|
|
135
|
+
*/
|
|
136
|
+
this.authenticationAttempted = true;
|
|
137
|
+
if (this.#emitter) {
|
|
138
|
+
this.#emitter.emit('basic_auth:authentication_attempted', {
|
|
139
|
+
ctx: this.#ctx,
|
|
140
|
+
guardName: this.#name,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Fetch credentials from the header
|
|
145
|
+
*/
|
|
146
|
+
const credentials = auth(this.#ctx.request.request);
|
|
147
|
+
if (!credentials) {
|
|
148
|
+
this.#authenticationFailed(AuthenticationException.E_INVALID_BASIC_AUTH_CREDENTIALS());
|
|
149
|
+
}
|
|
150
|
+
debug('basic_auth_guard: authenticating user using credentials');
|
|
151
|
+
/**
|
|
152
|
+
* Verifying user credentials
|
|
153
|
+
*/
|
|
154
|
+
this.user = await this.verifyCredentials(credentials.name, credentials.pass);
|
|
155
|
+
this.isAuthenticated = true;
|
|
156
|
+
debug('basic_auth_guard: marking user as authenticated');
|
|
157
|
+
if (this.#emitter) {
|
|
158
|
+
this.#emitter.emit('basic_auth:authentication_succeeded', {
|
|
159
|
+
ctx: this.#ctx,
|
|
160
|
+
guardName: this.#name,
|
|
161
|
+
user: this.user,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Return user
|
|
166
|
+
*/
|
|
167
|
+
return this.getUserOrFail();
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Silently attempt to authenticate the user.
|
|
171
|
+
*
|
|
172
|
+
* The method returns a boolean indicating if the authentication
|
|
173
|
+
* succeeded or failed.
|
|
174
|
+
*/
|
|
175
|
+
async check() {
|
|
176
|
+
try {
|
|
177
|
+
await this.authenticate();
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
if (error instanceof AuthenticationException) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Not support
|
|
189
|
+
*/
|
|
190
|
+
async authenticateAsClient(_) {
|
|
191
|
+
throw new RuntimeException('Cannot authenticate as a client when using basic auth');
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
* For the full copyright and license information, please view the LICENSE
|
|
7
7
|
* file that was distributed with this source code.
|
|
8
8
|
*/
|
|
9
|
-
|
|
10
|
-
export
|
|
9
|
+
export { BasicAuthGuard } from './guard.js';
|
|
10
|
+
export { basicAuthGuard } from './define_config.js';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Exception } from '@poppinss/utils';
|
|
2
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
3
|
+
/**
|
|
4
|
+
* Events emitted by the basic auth guard
|
|
5
|
+
*/
|
|
6
|
+
export type BasicAuthGuardEvents<User> = {
|
|
7
|
+
/**
|
|
8
|
+
* The event is emitted when the user credentials
|
|
9
|
+
* have been verified successfully.
|
|
10
|
+
*/
|
|
11
|
+
'basic_auth:credentials_verified': {
|
|
12
|
+
ctx: HttpContext;
|
|
13
|
+
guardName: string;
|
|
14
|
+
uid: string;
|
|
15
|
+
user: User;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Attempting to authenticate the user
|
|
19
|
+
*/
|
|
20
|
+
'basic_auth:authentication_attempted': {
|
|
21
|
+
ctx: HttpContext;
|
|
22
|
+
guardName: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Authentication was successful
|
|
26
|
+
*/
|
|
27
|
+
'basic_auth:authentication_succeeded': {
|
|
28
|
+
ctx: HttpContext;
|
|
29
|
+
guardName: string;
|
|
30
|
+
user: User;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Authentication failed
|
|
34
|
+
*/
|
|
35
|
+
'basic_auth:authentication_failed': {
|
|
36
|
+
ctx: HttpContext;
|
|
37
|
+
guardName: string;
|
|
38
|
+
error: Exception;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Emitter } from '@adonisjs/core/events';
|
|
2
1
|
import type { HttpContext } from '@adonisjs/core/http';
|
|
2
|
+
import type { EmitterLike } from '@adonisjs/core/types/events';
|
|
3
3
|
import type { GuardContract } from '../../auth/types.js';
|
|
4
4
|
import { GUARD_KNOWN_EVENTS, PROVIDER_REAL_USER } from '../../auth/symbols.js';
|
|
5
5
|
import type { SessionGuardEvents, SessionGuardConfig, RememberMeProviderContract, SessionUserProviderContract } from './types.js';
|
|
@@ -10,6 +10,10 @@ import type { SessionGuardEvents, SessionGuardConfig, RememberMeProviderContract
|
|
|
10
10
|
export declare class SessionGuard<UserProvider extends SessionUserProviderContract<unknown>> implements GuardContract<UserProvider[typeof PROVIDER_REAL_USER]> {
|
|
11
11
|
#private;
|
|
12
12
|
[GUARD_KNOWN_EVENTS]: SessionGuardEvents<UserProvider[typeof PROVIDER_REAL_USER]>;
|
|
13
|
+
/**
|
|
14
|
+
* Driver name of the guard
|
|
15
|
+
*/
|
|
16
|
+
driverName: 'session';
|
|
13
17
|
/**
|
|
14
18
|
* Whether or not the authentication has been attempted
|
|
15
19
|
* during the current request
|
|
@@ -67,12 +71,28 @@ export declare class SessionGuard<UserProvider extends SessionUserProviderContra
|
|
|
67
71
|
* Register an event emitter to listen for global events for
|
|
68
72
|
* authentication lifecycle.
|
|
69
73
|
*/
|
|
70
|
-
|
|
74
|
+
setEmitter(emitter: EmitterLike<any>): this;
|
|
71
75
|
/**
|
|
72
76
|
* Returns an instance of the authenticated user. Or throws
|
|
73
77
|
* an exception if the request is not authenticated.
|
|
74
78
|
*/
|
|
75
79
|
getUserOrFail(): UserProvider[typeof PROVIDER_REAL_USER];
|
|
80
|
+
/**
|
|
81
|
+
* Verifies user credentials and returns an instance of
|
|
82
|
+
* the user or throws "E_INVALID_CREDENTIALS" exception.
|
|
83
|
+
*/
|
|
84
|
+
verifyCredentials(uid: string, password: string): Promise<UserProvider[typeof PROVIDER_REAL_USER]>;
|
|
85
|
+
/**
|
|
86
|
+
* Attempt to login a user after verifying their
|
|
87
|
+
* credentials.
|
|
88
|
+
*/
|
|
89
|
+
attempt(uid: string, password: string, remember?: boolean): Promise<UserProvider[typeof PROVIDER_REAL_USER]>;
|
|
90
|
+
/**
|
|
91
|
+
* Attempt to login a user using the user id. The
|
|
92
|
+
* user will be first fetched from the db before
|
|
93
|
+
* marking them as logged-in
|
|
94
|
+
*/
|
|
95
|
+
loginViaId(id: string | number, remember?: boolean): Promise<UserProvider[typeof PROVIDER_REAL_USER]>;
|
|
76
96
|
/**
|
|
77
97
|
* Login a user using the user object.
|
|
78
98
|
*/
|
|
@@ -89,4 +109,15 @@ export declare class SessionGuard<UserProvider extends SessionUserProviderContra
|
|
|
89
109
|
* succeeded or failed.
|
|
90
110
|
*/
|
|
91
111
|
check(): Promise<boolean>;
|
|
112
|
+
/**
|
|
113
|
+
* Logout user and revoke remember me token (if any)
|
|
114
|
+
*/
|
|
115
|
+
logout(): Promise<void>;
|
|
116
|
+
/**
|
|
117
|
+
* Returns the session state for the user to be
|
|
118
|
+
* logged-in as a client
|
|
119
|
+
*/
|
|
120
|
+
authenticateAsClient(user: UserProvider[typeof PROVIDER_REAL_USER]): Promise<{
|
|
121
|
+
session: Record<string, string | number>;
|
|
122
|
+
}>;
|
|
92
123
|
}
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
import { RuntimeException } from '@poppinss/utils';
|
|
10
10
|
import debug from '../../auth/debug.js';
|
|
11
11
|
import { RememberMeToken } from './token.js';
|
|
12
|
-
import * as errors from '../../auth/errors.js';
|
|
13
12
|
import { GUARD_KNOWN_EVENTS } from '../../auth/symbols.js';
|
|
13
|
+
import { AuthenticationException, InvalidCredentialsException } from '../../auth/errors.js';
|
|
14
14
|
/**
|
|
15
15
|
* Session guard uses sessions and cookies to login and authenticate
|
|
16
16
|
* users.
|
|
@@ -42,6 +42,10 @@ export class SessionGuard {
|
|
|
42
42
|
* Emitter to emit events
|
|
43
43
|
*/
|
|
44
44
|
#emitter;
|
|
45
|
+
/**
|
|
46
|
+
* Driver name of the guard
|
|
47
|
+
*/
|
|
48
|
+
driverName = 'session';
|
|
45
49
|
/**
|
|
46
50
|
* Whether or not the authentication has been attempted
|
|
47
51
|
* during the current request
|
|
@@ -116,17 +120,33 @@ export class SessionGuard {
|
|
|
116
120
|
return this.#ctx.session;
|
|
117
121
|
}
|
|
118
122
|
/**
|
|
119
|
-
* Notifies about
|
|
123
|
+
* Notifies about authentication failure and throws the exception
|
|
120
124
|
*/
|
|
121
125
|
#authenticationFailed(error, sessionId) {
|
|
122
126
|
if (this.#emitter) {
|
|
123
127
|
this.#emitter.emit('session_auth:authentication_failed', {
|
|
128
|
+
ctx: this.#ctx,
|
|
129
|
+
guardName: this.#name,
|
|
124
130
|
error,
|
|
125
131
|
sessionId: sessionId,
|
|
126
132
|
});
|
|
127
133
|
}
|
|
128
134
|
throw error;
|
|
129
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Notifies about login failure and throws the exception
|
|
138
|
+
*/
|
|
139
|
+
#loginFailed(error, user) {
|
|
140
|
+
if (this.#emitter) {
|
|
141
|
+
this.#emitter.emit('session_auth:login_failed', {
|
|
142
|
+
ctx: this.#ctx,
|
|
143
|
+
guardName: this.#name,
|
|
144
|
+
error,
|
|
145
|
+
user,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
130
150
|
/**
|
|
131
151
|
* Register the remember me tokens provider to create
|
|
132
152
|
* remember me tokens during user login.
|
|
@@ -143,7 +163,7 @@ export class SessionGuard {
|
|
|
143
163
|
* Register an event emitter to listen for global events for
|
|
144
164
|
* authentication lifecycle.
|
|
145
165
|
*/
|
|
146
|
-
|
|
166
|
+
setEmitter(emitter) {
|
|
147
167
|
this.#emitter = emitter;
|
|
148
168
|
return this;
|
|
149
169
|
}
|
|
@@ -153,16 +173,78 @@ export class SessionGuard {
|
|
|
153
173
|
*/
|
|
154
174
|
getUserOrFail() {
|
|
155
175
|
if (!this.user) {
|
|
156
|
-
throw
|
|
176
|
+
throw AuthenticationException.E_INVALID_AUTH_SESSION();
|
|
157
177
|
}
|
|
158
178
|
return this.user;
|
|
159
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Verifies user credentials and returns an instance of
|
|
182
|
+
* the user or throws "E_INVALID_CREDENTIALS" exception.
|
|
183
|
+
*/
|
|
184
|
+
async verifyCredentials(uid, password) {
|
|
185
|
+
debug('session_guard: attempting to verify credentials for uid "%s"', uid);
|
|
186
|
+
/**
|
|
187
|
+
* Attempt to find a user by the uid and raise
|
|
188
|
+
* error when unable to find one
|
|
189
|
+
*/
|
|
190
|
+
const providerUser = await this.#userProvider.findByUid(uid);
|
|
191
|
+
if (!providerUser) {
|
|
192
|
+
this.#loginFailed(InvalidCredentialsException.E_INVALID_CREDENTIALS(this.driverName), null);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Raise error when unable to verify password
|
|
196
|
+
*/
|
|
197
|
+
const user = providerUser.getOriginal();
|
|
198
|
+
/**
|
|
199
|
+
* Raise error when unable to verify password
|
|
200
|
+
*/
|
|
201
|
+
if (!(await providerUser.verifyPassword(password))) {
|
|
202
|
+
this.#loginFailed(InvalidCredentialsException.E_INVALID_CREDENTIALS(this.driverName), user);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Notify credentials have been verified
|
|
206
|
+
*/
|
|
207
|
+
if (this.#emitter) {
|
|
208
|
+
this.#emitter.emit('session_auth:credentials_verified', {
|
|
209
|
+
ctx: this.#ctx,
|
|
210
|
+
guardName: this.#name,
|
|
211
|
+
uid,
|
|
212
|
+
user,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return user;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Attempt to login a user after verifying their
|
|
219
|
+
* credentials.
|
|
220
|
+
*/
|
|
221
|
+
async attempt(uid, password, remember) {
|
|
222
|
+
const user = await this.verifyCredentials(uid, password);
|
|
223
|
+
return this.login(user, remember);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Attempt to login a user using the user id. The
|
|
227
|
+
* user will be first fetched from the db before
|
|
228
|
+
* marking them as logged-in
|
|
229
|
+
*/
|
|
230
|
+
async loginViaId(id, remember) {
|
|
231
|
+
debug('session_guard: attempting to login user via id "%s"', id);
|
|
232
|
+
const providerUser = await this.#userProvider.findById(id);
|
|
233
|
+
if (!providerUser) {
|
|
234
|
+
this.#loginFailed(InvalidCredentialsException.E_INVALID_CREDENTIALS(this.driverName), null);
|
|
235
|
+
}
|
|
236
|
+
return this.login(providerUser.getOriginal(), remember);
|
|
237
|
+
}
|
|
160
238
|
/**
|
|
161
239
|
* Login a user using the user object.
|
|
162
240
|
*/
|
|
163
241
|
async login(user, remember = false) {
|
|
164
242
|
if (this.#emitter) {
|
|
165
|
-
this.#emitter.emit('session_auth:login_attempted', {
|
|
243
|
+
this.#emitter.emit('session_auth:login_attempted', {
|
|
244
|
+
ctx: this.#ctx,
|
|
245
|
+
user,
|
|
246
|
+
guardName: this.#name,
|
|
247
|
+
});
|
|
166
248
|
}
|
|
167
249
|
const providerUser = await this.#userProvider.createUserForGuard(user);
|
|
168
250
|
const session = this.#getSession();
|
|
@@ -209,6 +291,8 @@ export class SessionGuard {
|
|
|
209
291
|
*/
|
|
210
292
|
if (this.#emitter) {
|
|
211
293
|
this.#emitter.emit('session_auth:login_succeeded', {
|
|
294
|
+
ctx: this.#ctx,
|
|
295
|
+
guardName: this.#name,
|
|
212
296
|
user,
|
|
213
297
|
sessionId: session.sessionId,
|
|
214
298
|
rememberMeToken: token,
|
|
@@ -231,6 +315,8 @@ export class SessionGuard {
|
|
|
231
315
|
*/
|
|
232
316
|
if (this.#emitter) {
|
|
233
317
|
this.#emitter.emit('session_auth:authentication_attempted', {
|
|
318
|
+
ctx: this.#ctx,
|
|
319
|
+
guardName: this.#name,
|
|
234
320
|
sessionId: session.sessionId,
|
|
235
321
|
});
|
|
236
322
|
}
|
|
@@ -248,8 +334,9 @@ export class SessionGuard {
|
|
|
248
334
|
* storage
|
|
249
335
|
*/
|
|
250
336
|
if (!providerUser) {
|
|
251
|
-
this.#authenticationFailed(
|
|
337
|
+
this.#authenticationFailed(AuthenticationException.E_INVALID_AUTH_SESSION(), session.sessionId);
|
|
252
338
|
}
|
|
339
|
+
debug('session_guard: marking user with id "%s" as authenticated', providerUser.getId());
|
|
253
340
|
this.user = providerUser.getOriginal();
|
|
254
341
|
this.isAuthenticated = true;
|
|
255
342
|
this.isLoggedOut = false;
|
|
@@ -259,6 +346,8 @@ export class SessionGuard {
|
|
|
259
346
|
*/
|
|
260
347
|
if (this.#emitter) {
|
|
261
348
|
this.#emitter.emit('session_auth:authentication_succeeded', {
|
|
349
|
+
ctx: this.#ctx,
|
|
350
|
+
guardName: this.#name,
|
|
262
351
|
sessionId: session.sessionId,
|
|
263
352
|
user: this.user,
|
|
264
353
|
});
|
|
@@ -280,7 +369,7 @@ export class SessionGuard {
|
|
|
280
369
|
*/
|
|
281
370
|
const rememberMeCookie = this.#ctx.request.encryptedCookie(this.rememberMeKeyName);
|
|
282
371
|
if (!rememberMeCookie || !this.#rememberMeTokenProvider) {
|
|
283
|
-
this.#authenticationFailed(
|
|
372
|
+
this.#authenticationFailed(AuthenticationException.E_INVALID_AUTH_SESSION(), session.sessionId);
|
|
284
373
|
}
|
|
285
374
|
debug('session_guard: authenticating user from remember me cookie');
|
|
286
375
|
/**
|
|
@@ -289,9 +378,12 @@ export class SessionGuard {
|
|
|
289
378
|
* is missing or invalid
|
|
290
379
|
*/
|
|
291
380
|
const decodedToken = RememberMeToken.decode(rememberMeCookie);
|
|
381
|
+
if (!decodedToken) {
|
|
382
|
+
this.#authenticationFailed(AuthenticationException.E_INVALID_AUTH_SESSION(), session.sessionId);
|
|
383
|
+
}
|
|
292
384
|
const token = await this.#rememberMeTokenProvider.getTokenBySeries(decodedToken.series);
|
|
293
385
|
if (!token || !token.verify(decodedToken.value)) {
|
|
294
|
-
this.#authenticationFailed(
|
|
386
|
+
this.#authenticationFailed(AuthenticationException.E_INVALID_AUTH_SESSION(), session.sessionId);
|
|
295
387
|
}
|
|
296
388
|
debug('session_guard: found valid remember me token');
|
|
297
389
|
/**
|
|
@@ -300,7 +392,7 @@ export class SessionGuard {
|
|
|
300
392
|
*/
|
|
301
393
|
const providerUser = await this.#userProvider.findById(token.userId);
|
|
302
394
|
if (!providerUser) {
|
|
303
|
-
this.#authenticationFailed(
|
|
395
|
+
this.#authenticationFailed(AuthenticationException.E_INVALID_AUTH_SESSION(), session.sessionId);
|
|
304
396
|
}
|
|
305
397
|
/**
|
|
306
398
|
* Finally, login the user from the remember me token
|
|
@@ -309,6 +401,7 @@ export class SessionGuard {
|
|
|
309
401
|
debug('session_guard: marking user with id "%s" as logged in from remember me cookie', userId);
|
|
310
402
|
session.put(this.sessionKeyName, userId);
|
|
311
403
|
session.regenerate();
|
|
404
|
+
debug('session_guard: marking user with id "%s" as authenticated', userId);
|
|
312
405
|
this.user = providerUser.getOriginal();
|
|
313
406
|
this.isAuthenticated = true;
|
|
314
407
|
this.isLoggedOut = false;
|
|
@@ -318,6 +411,8 @@ export class SessionGuard {
|
|
|
318
411
|
*/
|
|
319
412
|
if (this.#emitter) {
|
|
320
413
|
this.#emitter.emit('session_auth:authentication_succeeded', {
|
|
414
|
+
ctx: this.#ctx,
|
|
415
|
+
guardName: this.#name,
|
|
321
416
|
sessionId: session.sessionId,
|
|
322
417
|
user: this.user,
|
|
323
418
|
rememberMeToken: token,
|
|
@@ -370,11 +465,58 @@ export class SessionGuard {
|
|
|
370
465
|
return true;
|
|
371
466
|
}
|
|
372
467
|
catch (error) {
|
|
373
|
-
if (error instanceof
|
|
374
|
-
error instanceof errors.E_INVALID_AUTH_TOKEN) {
|
|
468
|
+
if (error instanceof AuthenticationException) {
|
|
375
469
|
return false;
|
|
376
470
|
}
|
|
377
471
|
throw error;
|
|
378
472
|
}
|
|
379
473
|
}
|
|
474
|
+
/**
|
|
475
|
+
* Logout user and revoke remember me token (if any)
|
|
476
|
+
*/
|
|
477
|
+
async logout() {
|
|
478
|
+
debug('session_auth: logging out');
|
|
479
|
+
const session = this.#getSession();
|
|
480
|
+
/**
|
|
481
|
+
* Clear client side state
|
|
482
|
+
*/
|
|
483
|
+
session.forget(this.sessionKeyName);
|
|
484
|
+
this.#ctx.response.clearCookie(this.rememberMeKeyName);
|
|
485
|
+
/**
|
|
486
|
+
* Notify the user has been logged out
|
|
487
|
+
*/
|
|
488
|
+
if (this.#emitter) {
|
|
489
|
+
this.#emitter.emit('session_auth:logged_out', {
|
|
490
|
+
ctx: this.#ctx,
|
|
491
|
+
guardName: this.#name,
|
|
492
|
+
user: this.user || null,
|
|
493
|
+
sessionId: session.sessionId,
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
const rememberMeCookie = this.#ctx.request.encryptedCookie(this.rememberMeKeyName);
|
|
497
|
+
if (!rememberMeCookie || !this.#rememberMeTokenProvider) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
debug('session_auth: decoding remember me token');
|
|
501
|
+
const decodedToken = RememberMeToken.decode(rememberMeCookie);
|
|
502
|
+
if (!decodedToken) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
debug('session_auth: deleting remember me token');
|
|
506
|
+
await this.#rememberMeTokenProvider.deleteTokenBySeries(decodedToken.series);
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Returns the session state for the user to be
|
|
510
|
+
* logged-in as a client
|
|
511
|
+
*/
|
|
512
|
+
async authenticateAsClient(user) {
|
|
513
|
+
const providerUser = await this.#userProvider.createUserForGuard(user);
|
|
514
|
+
const userId = providerUser.getId();
|
|
515
|
+
debug('session_guard: returning client session for user id "%s"', userId);
|
|
516
|
+
return {
|
|
517
|
+
session: {
|
|
518
|
+
[this.sessionKeyName]: userId,
|
|
519
|
+
},
|
|
520
|
+
};
|
|
521
|
+
}
|
|
380
522
|
}
|