@adonisjs/auth 9.0.0-0 → 9.0.0-1
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 +14 -0
- package/build/src/auth/auth_manager.d.ts +4 -0
- package/build/src/auth/auth_manager.js +6 -0
- package/build/src/auth/authenticator.d.ts +38 -0
- package/build/src/auth/authenticator.js +67 -0
- package/build/src/auth/errors.d.ts +78 -4
- package/build/src/auth/errors.js +169 -5
- package/build/src/auth/middleware/auth_middleware.d.ts +13 -0
- package/build/src/auth/middleware/auth_middleware.js +6 -0
- 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/types.d.ts +31 -8
- package/build/src/core/token.d.ts +4 -1
- package/build/src/core/token.js +5 -3
- package/build/src/guards/session/guard.d.ts +20 -0
- package/build/src/guards/session/guard.js +83 -9
- package/build/src/guards/session/types.d.ts +0 -1
- package/build/stubs/config.stub +1 -1
- package/package.json +5 -2
- package/build/stubs/config/auth_middleware.stub +0 -12
package/build/configure.js
CHANGED
|
@@ -38,4 +38,18 @@ export async function configure(command) {
|
|
|
38
38
|
await codemods.updateRcFile((rcFile) => {
|
|
39
39
|
rcFile.addProvider('@adonisjs/auth/auth_provider');
|
|
40
40
|
});
|
|
41
|
+
/**
|
|
42
|
+
* Register middleware
|
|
43
|
+
*/
|
|
44
|
+
await codemods.registerMiddleware('router', [
|
|
45
|
+
{
|
|
46
|
+
path: '@adonisjs/auth/initialize_auth_middleware',
|
|
47
|
+
},
|
|
48
|
+
]);
|
|
49
|
+
await codemods.registerMiddleware('named', [
|
|
50
|
+
{
|
|
51
|
+
name: 'auth',
|
|
52
|
+
path: '@adonisjs/auth/auth_middleware',
|
|
53
|
+
},
|
|
54
|
+
]);
|
|
41
55
|
}
|
|
@@ -7,6 +7,10 @@ import { Authenticator } from './authenticator.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export declare class AuthManager<KnownGuards extends Record<string, GuardFactory>> {
|
|
9
9
|
#private;
|
|
10
|
+
/**
|
|
11
|
+
* Name of the default guard
|
|
12
|
+
*/
|
|
13
|
+
get defaultGuard(): keyof KnownGuards;
|
|
10
14
|
constructor(config: {
|
|
11
15
|
default: keyof KnownGuards;
|
|
12
16
|
guards: KnownGuards;
|
|
@@ -6,6 +6,31 @@ import type { GuardFactory } from './types.js';
|
|
|
6
6
|
*/
|
|
7
7
|
export declare class Authenticator<KnownGuards extends Record<string, GuardFactory>> {
|
|
8
8
|
#private;
|
|
9
|
+
/**
|
|
10
|
+
* Name of the default guard
|
|
11
|
+
*/
|
|
12
|
+
get defaultGuard(): keyof KnownGuards;
|
|
13
|
+
/**
|
|
14
|
+
* Reference to the guard using which the current
|
|
15
|
+
* request has been authenticated.
|
|
16
|
+
*/
|
|
17
|
+
get authenticatedViaGuard(): keyof KnownGuards | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* A boolean to know if the current request has
|
|
20
|
+
* been authenticated
|
|
21
|
+
*/
|
|
22
|
+
get isAuthenticated(): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Reference to the currently authenticated user
|
|
25
|
+
*/
|
|
26
|
+
get user(): {
|
|
27
|
+
[K in keyof KnownGuards]: ReturnType<KnownGuards[K]>['user'];
|
|
28
|
+
}[keyof KnownGuards];
|
|
29
|
+
/**
|
|
30
|
+
* Whether or not the authentication has been attempted
|
|
31
|
+
* during the current request
|
|
32
|
+
*/
|
|
33
|
+
get authenticationAttempted(): boolean;
|
|
9
34
|
constructor(ctx: HttpContext, config: {
|
|
10
35
|
default: keyof KnownGuards;
|
|
11
36
|
guards: KnownGuards;
|
|
@@ -15,4 +40,17 @@ export declare class Authenticator<KnownGuards extends Record<string, GuardFacto
|
|
|
15
40
|
* cached during the lifecycle of an HTTP request.
|
|
16
41
|
*/
|
|
17
42
|
use<Guard extends keyof KnownGuards>(guard?: Guard): ReturnType<KnownGuards[Guard]>;
|
|
43
|
+
/**
|
|
44
|
+
* Authenticate the request using all of the mentioned
|
|
45
|
+
* guards or the default guard.
|
|
46
|
+
*
|
|
47
|
+
* The authentication process will stop after any of the
|
|
48
|
+
* mentioned guards is able to authenticate the request
|
|
49
|
+
* successfully.
|
|
50
|
+
*
|
|
51
|
+
* Otherwise, "AuthenticationException" will be raised.
|
|
52
|
+
*/
|
|
53
|
+
authenticateUsing(guards?: (keyof KnownGuards)[], options?: {
|
|
54
|
+
redirectTo?: string;
|
|
55
|
+
}): Promise<boolean>;
|
|
18
56
|
}
|
|
@@ -7,11 +7,17 @@
|
|
|
7
7
|
* file that was distributed with this source code.
|
|
8
8
|
*/
|
|
9
9
|
import debug from './debug.js';
|
|
10
|
+
import { AuthenticationException } from './errors.js';
|
|
10
11
|
/**
|
|
11
12
|
* Authenticator is an HTTP request specific implementation for using
|
|
12
13
|
* guards to login users and authenticate requests.
|
|
13
14
|
*/
|
|
14
15
|
export class Authenticator {
|
|
16
|
+
/**
|
|
17
|
+
* Name of the guard using which the request has
|
|
18
|
+
* been authenticated
|
|
19
|
+
*/
|
|
20
|
+
#authenticatedViaGuard;
|
|
15
21
|
/**
|
|
16
22
|
* Reference to HTTP context
|
|
17
23
|
*/
|
|
@@ -24,6 +30,39 @@ export class Authenticator {
|
|
|
24
30
|
* Cache of guards created during the HTTP request
|
|
25
31
|
*/
|
|
26
32
|
#guardsCache = {};
|
|
33
|
+
/**
|
|
34
|
+
* Name of the default guard
|
|
35
|
+
*/
|
|
36
|
+
get defaultGuard() {
|
|
37
|
+
return this.#config.default;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Reference to the guard using which the current
|
|
41
|
+
* request has been authenticated.
|
|
42
|
+
*/
|
|
43
|
+
get authenticatedViaGuard() {
|
|
44
|
+
return this.#authenticatedViaGuard;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A boolean to know if the current request has
|
|
48
|
+
* been authenticated
|
|
49
|
+
*/
|
|
50
|
+
get isAuthenticated() {
|
|
51
|
+
return this.use(this.#authenticatedViaGuard || this.defaultGuard).isAuthenticated;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Reference to the currently authenticated user
|
|
55
|
+
*/
|
|
56
|
+
get user() {
|
|
57
|
+
return this.use(this.#authenticatedViaGuard || this.defaultGuard).user;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Whether or not the authentication has been attempted
|
|
61
|
+
* during the current request
|
|
62
|
+
*/
|
|
63
|
+
get authenticationAttempted() {
|
|
64
|
+
return this.use(this.#authenticatedViaGuard || this.defaultGuard).authenticationAttempted;
|
|
65
|
+
}
|
|
27
66
|
constructor(ctx, config) {
|
|
28
67
|
this.#ctx = ctx;
|
|
29
68
|
this.#config = config;
|
|
@@ -52,4 +91,32 @@ export class Authenticator {
|
|
|
52
91
|
this.#guardsCache[guardToUse] = guardInstance;
|
|
53
92
|
return guardInstance;
|
|
54
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Authenticate the request using all of the mentioned
|
|
96
|
+
* guards or the default guard.
|
|
97
|
+
*
|
|
98
|
+
* The authentication process will stop after any of the
|
|
99
|
+
* mentioned guards is able to authenticate the request
|
|
100
|
+
* successfully.
|
|
101
|
+
*
|
|
102
|
+
* Otherwise, "AuthenticationException" will be raised.
|
|
103
|
+
*/
|
|
104
|
+
async authenticateUsing(guards, options) {
|
|
105
|
+
const guardsToUse = guards || [this.defaultGuard];
|
|
106
|
+
let lastUsedGuardDriver;
|
|
107
|
+
for (let guardName of guardsToUse) {
|
|
108
|
+
debug('attempting to authenticate using guard "%s"', guardName);
|
|
109
|
+
const guard = this.use(guardName);
|
|
110
|
+
lastUsedGuardDriver = guard.driverName;
|
|
111
|
+
if (await guard.check()) {
|
|
112
|
+
this.#authenticatedViaGuard = guardName;
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
throw new AuthenticationException('Unauthorized access', {
|
|
117
|
+
code: 'E_UNAUTHORIZED_ACCESS',
|
|
118
|
+
guardDriverName: lastUsedGuardDriver,
|
|
119
|
+
redirectTo: options?.redirectTo,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
55
122
|
}
|
|
@@ -1,8 +1,82 @@
|
|
|
1
|
+
import { Exception } from '@poppinss/utils';
|
|
2
|
+
import { HttpContext } from '@adonisjs/core/http';
|
|
1
3
|
/**
|
|
2
|
-
*
|
|
4
|
+
* Authentication exception is raised when an attempt is
|
|
5
|
+
* made to authenticate an HTTP request
|
|
3
6
|
*/
|
|
4
|
-
export declare
|
|
7
|
+
export declare class AuthenticationException extends Exception {
|
|
8
|
+
/**
|
|
9
|
+
* Raises authentication exception when session guard
|
|
10
|
+
* is unable to authenticate the request
|
|
11
|
+
*/
|
|
12
|
+
static E_INVALID_AUTH_SESSION(): AuthenticationException;
|
|
13
|
+
guardDriverName: string;
|
|
14
|
+
redirectTo?: string;
|
|
15
|
+
identifier: string;
|
|
16
|
+
constructor(message: string, options: ErrorOptions & {
|
|
17
|
+
guardDriverName: string;
|
|
18
|
+
redirectTo?: string;
|
|
19
|
+
code?: string;
|
|
20
|
+
status?: number;
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* Returns the message to be sent in the HTTP response.
|
|
24
|
+
* Feel free to override this method and return a custom
|
|
25
|
+
* response.
|
|
26
|
+
*/
|
|
27
|
+
getResponseMessage(error: AuthenticationException, ctx: HttpContext): string;
|
|
28
|
+
/**
|
|
29
|
+
* A collection of authentication exception
|
|
30
|
+
* renderers to render the exception to a
|
|
31
|
+
* response.
|
|
32
|
+
*
|
|
33
|
+
* The collection is a key-value pair, where the
|
|
34
|
+
* key is the guard driver name and value is
|
|
35
|
+
* a factory function to respond to the
|
|
36
|
+
* request.
|
|
37
|
+
*/
|
|
38
|
+
renderers: Record<string, (message: string, error: AuthenticationException, ctx: HttpContext) => Promise<void> | void>;
|
|
39
|
+
/**
|
|
40
|
+
* Self handles the auth exception and converts it to an
|
|
41
|
+
* HTTP response
|
|
42
|
+
*/
|
|
43
|
+
handle(error: AuthenticationException, ctx: HttpContext): Promise<void>;
|
|
44
|
+
}
|
|
5
45
|
/**
|
|
6
|
-
*
|
|
46
|
+
* Invalid credentials exception is raised when unable
|
|
47
|
+
* to verify user credentials during login
|
|
7
48
|
*/
|
|
8
|
-
export declare
|
|
49
|
+
export declare class InvalidCredentialsException extends Exception {
|
|
50
|
+
static message: string;
|
|
51
|
+
static code: string;
|
|
52
|
+
static E_INVALID_CREDENTIALS(guardDriverName: string): InvalidCredentialsException;
|
|
53
|
+
guardDriverName: string;
|
|
54
|
+
identifier: string;
|
|
55
|
+
constructor(message: string, options: ErrorOptions & {
|
|
56
|
+
guardDriverName: string;
|
|
57
|
+
code?: string;
|
|
58
|
+
status?: number;
|
|
59
|
+
});
|
|
60
|
+
/**
|
|
61
|
+
* Returns the message to be sent in the HTTP response.
|
|
62
|
+
* Feel free to override this method and return a custom
|
|
63
|
+
* response.
|
|
64
|
+
*/
|
|
65
|
+
getResponseMessage(error: InvalidCredentialsException, ctx: HttpContext): string;
|
|
66
|
+
/**
|
|
67
|
+
* A collection of authentication exception
|
|
68
|
+
* renderers to render the exception to a
|
|
69
|
+
* response.
|
|
70
|
+
*
|
|
71
|
+
* The collection is a key-value pair, where the
|
|
72
|
+
* key is the guard driver name and value is
|
|
73
|
+
* a factory function to respond to the
|
|
74
|
+
* request.
|
|
75
|
+
*/
|
|
76
|
+
renderers: Record<string, (message: string, error: InvalidCredentialsException, ctx: HttpContext) => Promise<void> | void>;
|
|
77
|
+
/**
|
|
78
|
+
* Self handles the auth exception and converts it to an
|
|
79
|
+
* HTTP response
|
|
80
|
+
*/
|
|
81
|
+
handle(error: InvalidCredentialsException, ctx: HttpContext): Promise<void>;
|
|
82
|
+
}
|
package/build/src/auth/errors.js
CHANGED
|
@@ -6,12 +6,176 @@
|
|
|
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
|
-
import {
|
|
9
|
+
import { Exception } from '@poppinss/utils';
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Authentication exception is raised when an attempt is
|
|
12
|
+
* made to authenticate an HTTP request
|
|
12
13
|
*/
|
|
13
|
-
export
|
|
14
|
+
export class AuthenticationException extends Exception {
|
|
15
|
+
/**
|
|
16
|
+
* Raises authentication exception when session guard
|
|
17
|
+
* is unable to authenticate the request
|
|
18
|
+
*/
|
|
19
|
+
static E_INVALID_AUTH_SESSION() {
|
|
20
|
+
return new AuthenticationException('Invalid or expired authentication session', {
|
|
21
|
+
code: 'E_INVALID_AUTH_SESSION',
|
|
22
|
+
status: 401,
|
|
23
|
+
guardDriverName: 'session',
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
guardDriverName;
|
|
27
|
+
redirectTo;
|
|
28
|
+
identifier = 'auth.authenticate';
|
|
29
|
+
constructor(message, options) {
|
|
30
|
+
super(message, options);
|
|
31
|
+
this.guardDriverName = options.guardDriverName;
|
|
32
|
+
this.redirectTo = options.redirectTo;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Returns the message to be sent in the HTTP response.
|
|
36
|
+
* Feel free to override this method and return a custom
|
|
37
|
+
* response.
|
|
38
|
+
*/
|
|
39
|
+
getResponseMessage(error, ctx) {
|
|
40
|
+
if ('i18n' in ctx) {
|
|
41
|
+
return ctx.i18n.t(error.identifier, {}, error.message);
|
|
42
|
+
}
|
|
43
|
+
return error.message;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A collection of authentication exception
|
|
47
|
+
* renderers to render the exception to a
|
|
48
|
+
* response.
|
|
49
|
+
*
|
|
50
|
+
* The collection is a key-value pair, where the
|
|
51
|
+
* key is the guard driver name and value is
|
|
52
|
+
* a factory function to respond to the
|
|
53
|
+
* request.
|
|
54
|
+
*/
|
|
55
|
+
renderers = {
|
|
56
|
+
session: (message, error, ctx) => {
|
|
57
|
+
switch (ctx.request.accepts(['html', 'application/vnd.api+json', 'json'])) {
|
|
58
|
+
case 'html':
|
|
59
|
+
case null:
|
|
60
|
+
ctx.session.flashExcept(['_csrf']);
|
|
61
|
+
ctx.session.flash({ errors: { [error.identifier]: [message] } });
|
|
62
|
+
ctx.response.redirect(error.redirectTo || '/', true);
|
|
63
|
+
break;
|
|
64
|
+
case 'json':
|
|
65
|
+
ctx.response.status(error.status).send({
|
|
66
|
+
errors: [
|
|
67
|
+
{
|
|
68
|
+
message,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
break;
|
|
73
|
+
case 'application/vnd.api+json':
|
|
74
|
+
ctx.response.status(error.status).send({
|
|
75
|
+
errors: [
|
|
76
|
+
{
|
|
77
|
+
code: error.identifier,
|
|
78
|
+
title: message,
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
});
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Self handles the auth exception and converts it to an
|
|
88
|
+
* HTTP response
|
|
89
|
+
*/
|
|
90
|
+
async handle(error, ctx) {
|
|
91
|
+
const renderer = this.renderers[this.guardDriverName];
|
|
92
|
+
const message = error.getResponseMessage(error, ctx);
|
|
93
|
+
if (!renderer) {
|
|
94
|
+
return ctx.response.status(error.status).send(message);
|
|
95
|
+
}
|
|
96
|
+
return renderer(message, error, ctx);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
14
99
|
/**
|
|
15
|
-
*
|
|
100
|
+
* Invalid credentials exception is raised when unable
|
|
101
|
+
* to verify user credentials during login
|
|
16
102
|
*/
|
|
17
|
-
export
|
|
103
|
+
export class InvalidCredentialsException extends Exception {
|
|
104
|
+
static message = 'Invalid credentials';
|
|
105
|
+
static code = 'E_INVALID_CREDENTIALS';
|
|
106
|
+
static E_INVALID_CREDENTIALS(guardDriverName) {
|
|
107
|
+
return new InvalidCredentialsException(InvalidCredentialsException.message, {
|
|
108
|
+
guardDriverName,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
guardDriverName;
|
|
112
|
+
identifier = 'auth.login';
|
|
113
|
+
constructor(message, options) {
|
|
114
|
+
super(message, options);
|
|
115
|
+
this.guardDriverName = options.guardDriverName;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Returns the message to be sent in the HTTP response.
|
|
119
|
+
* Feel free to override this method and return a custom
|
|
120
|
+
* response.
|
|
121
|
+
*/
|
|
122
|
+
getResponseMessage(error, ctx) {
|
|
123
|
+
if ('i18n' in ctx) {
|
|
124
|
+
return ctx.i18n.t(this.identifier, {}, error.message);
|
|
125
|
+
}
|
|
126
|
+
return error.message;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* A collection of authentication exception
|
|
130
|
+
* renderers to render the exception to a
|
|
131
|
+
* response.
|
|
132
|
+
*
|
|
133
|
+
* The collection is a key-value pair, where the
|
|
134
|
+
* key is the guard driver name and value is
|
|
135
|
+
* a factory function to respond to the
|
|
136
|
+
* request.
|
|
137
|
+
*/
|
|
138
|
+
renderers = {
|
|
139
|
+
session: (message, error, ctx) => {
|
|
140
|
+
switch (ctx.request.accepts(['html', 'application/vnd.api+json', 'json'])) {
|
|
141
|
+
case 'html':
|
|
142
|
+
case null:
|
|
143
|
+
ctx.session.flashExcept(['_csrf']);
|
|
144
|
+
ctx.session.flash({ errors: { [this.identifier]: [message] } });
|
|
145
|
+
ctx.response.redirect().withQs().back();
|
|
146
|
+
break;
|
|
147
|
+
case 'json':
|
|
148
|
+
ctx.response.status(error.status).send({
|
|
149
|
+
errors: [
|
|
150
|
+
{
|
|
151
|
+
message: message,
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
});
|
|
155
|
+
break;
|
|
156
|
+
case 'application/vnd.api+json':
|
|
157
|
+
ctx.response.status(error.status).send({
|
|
158
|
+
errors: [
|
|
159
|
+
{
|
|
160
|
+
code: this.identifier,
|
|
161
|
+
title: message,
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
});
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* Self handles the auth exception and converts it to an
|
|
171
|
+
* HTTP response
|
|
172
|
+
*/
|
|
173
|
+
async handle(error, ctx) {
|
|
174
|
+
const renderer = this.renderers[this.guardDriverName];
|
|
175
|
+
const message = this.getResponseMessage(error, ctx);
|
|
176
|
+
if (!renderer) {
|
|
177
|
+
return ctx.response.status(error.status).send(message);
|
|
178
|
+
}
|
|
179
|
+
return renderer(message, error, ctx);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
2
|
+
import type { NextFn } from '@adonisjs/core/types/http';
|
|
3
|
+
import type { Authenticators } from '@adonisjs/auth/types';
|
|
4
|
+
/**
|
|
5
|
+
* Options accepted by the middleware options
|
|
6
|
+
*/
|
|
7
|
+
export type AuthMiddlewareOptions = {
|
|
8
|
+
guards?: (keyof Authenticators)[];
|
|
9
|
+
redirectTo?: string;
|
|
10
|
+
};
|
|
11
|
+
export default class AuthMiddleware {
|
|
12
|
+
handle(ctx: HttpContext, next: NextFn, options?: AuthMiddlewareOptions): Promise<any>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import auth from '@adonisjs/auth/services/main';
|
|
2
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
3
|
+
import type { NextFn } from '@adonisjs/core/types/http';
|
|
4
|
+
/**
|
|
5
|
+
* The "InitializeAuthMiddleware" is used to create a request
|
|
6
|
+
* specific authenticator instance for every HTTP request.
|
|
7
|
+
*
|
|
8
|
+
* This middleware does not protect routes from unauthenticated
|
|
9
|
+
* users. Please use the "auth" middleware for that.
|
|
10
|
+
*/
|
|
11
|
+
export default class InitializeAuthMiddleware {
|
|
12
|
+
handle(ctx: HttpContext, next: NextFn): Promise<any>;
|
|
13
|
+
}
|
|
14
|
+
declare module '@adonisjs/core/http' {
|
|
15
|
+
interface HttpContext {
|
|
16
|
+
auth: ReturnType<(typeof auth)['createAuthenticator']>;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/// <reference types="@adonisjs/core/providers/edge_provider" />
|
|
2
|
+
import auth from '@adonisjs/auth/services/main';
|
|
3
|
+
/**
|
|
4
|
+
* The "InitializeAuthMiddleware" is used to create a request
|
|
5
|
+
* specific authenticator instance for every HTTP request.
|
|
6
|
+
*
|
|
7
|
+
* This middleware does not protect routes from unauthenticated
|
|
8
|
+
* users. Please use the "auth" middleware for that.
|
|
9
|
+
*/
|
|
10
|
+
export default class InitializeAuthMiddleware {
|
|
11
|
+
async handle(ctx, next) {
|
|
12
|
+
/**
|
|
13
|
+
* Initialize the authenticator for the current HTTP
|
|
14
|
+
* request
|
|
15
|
+
*/
|
|
16
|
+
ctx.auth = auth.createAuthenticator(ctx);
|
|
17
|
+
/**
|
|
18
|
+
* Sharing authenticator with templates
|
|
19
|
+
*/
|
|
20
|
+
if ('view' in ctx) {
|
|
21
|
+
ctx.view.share({ auth: ctx.auth });
|
|
22
|
+
}
|
|
23
|
+
return next();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Emitter } from '@adonisjs/core/events';
|
|
2
1
|
import type { HttpContext } from '@adonisjs/core/http';
|
|
3
2
|
import type { ApplicationService, ConfigProvider } from '@adonisjs/core/types';
|
|
4
3
|
import type { AuthManager } from './auth_manager.js';
|
|
@@ -8,18 +7,39 @@ import type { GUARD_KNOWN_EVENTS } from './symbols.js';
|
|
|
8
7
|
*/
|
|
9
8
|
export interface GuardContract<User> {
|
|
10
9
|
/**
|
|
11
|
-
* Reference to the user
|
|
10
|
+
* Reference to the currently authenticated user
|
|
12
11
|
*/
|
|
13
12
|
user?: User;
|
|
13
|
+
/**
|
|
14
|
+
* A boolean to know if the current request has
|
|
15
|
+
* been authenticated
|
|
16
|
+
*/
|
|
17
|
+
isAuthenticated: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Whether or not the authentication has been attempted
|
|
20
|
+
* during the current request
|
|
21
|
+
*/
|
|
22
|
+
authenticationAttempted: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Check if the current request has been
|
|
25
|
+
* authenticated without throwing an
|
|
26
|
+
* exception
|
|
27
|
+
*/
|
|
28
|
+
check(): Promise<boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* Authenticates the current request and throws
|
|
31
|
+
* an exception if the request is not authenticated.
|
|
32
|
+
*/
|
|
33
|
+
authenticate(): Promise<User>;
|
|
34
|
+
/**
|
|
35
|
+
* A unique name for the guard driver
|
|
36
|
+
*/
|
|
37
|
+
driverName: string;
|
|
14
38
|
/**
|
|
15
39
|
* Aymbol for infer the events emitted by a specific
|
|
16
40
|
* guard
|
|
17
41
|
*/
|
|
18
42
|
[GUARD_KNOWN_EVENTS]: unknown;
|
|
19
|
-
/**
|
|
20
|
-
* Accept an instance of the emitter to emit events
|
|
21
|
-
*/
|
|
22
|
-
withEmitter(emitter: Emitter<any>): this;
|
|
23
43
|
}
|
|
24
44
|
/**
|
|
25
45
|
* The authenticator guard factory method is called by the
|
|
@@ -36,13 +56,16 @@ export interface Authenticators {
|
|
|
36
56
|
/**
|
|
37
57
|
* Infer authenticators from the auth config
|
|
38
58
|
*/
|
|
39
|
-
export type InferAuthenticators<Config extends ConfigProvider<
|
|
59
|
+
export type InferAuthenticators<Config extends ConfigProvider<{
|
|
60
|
+
default: unknown;
|
|
61
|
+
guards: unknown;
|
|
62
|
+
}>> = Awaited<ReturnType<Config['resolver']>>['guards'];
|
|
40
63
|
/**
|
|
41
64
|
* Auth service is a singleton instance of the AuthManager
|
|
42
65
|
* configured using the config stored within the user
|
|
43
66
|
* app.
|
|
44
67
|
*/
|
|
45
|
-
export interface AuthService extends AuthManager<Authenticators extends GuardFactory ? Authenticators : never> {
|
|
68
|
+
export interface AuthService extends AuthManager<Authenticators extends Record<string, GuardFactory> ? Authenticators : never> {
|
|
46
69
|
}
|
|
47
70
|
/**
|
|
48
71
|
* Config provider for exporting guard
|
|
@@ -78,8 +78,11 @@ export declare abstract class Token implements TokenContract {
|
|
|
78
78
|
/**
|
|
79
79
|
* Decodes a publicly shared token and return the series
|
|
80
80
|
* and the token value from it.
|
|
81
|
+
*
|
|
82
|
+
* Returns null when unable to decode the token because of
|
|
83
|
+
* invalid format or encoding.
|
|
81
84
|
*/
|
|
82
|
-
static decode(value: string): {
|
|
85
|
+
static decode(value: string): null | {
|
|
83
86
|
series: string;
|
|
84
87
|
value: string;
|
|
85
88
|
};
|
package/build/src/core/token.js
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
import { createHash } from 'node:crypto';
|
|
10
10
|
import string from '@adonisjs/core/helpers/string';
|
|
11
11
|
import { base64, safeEqual } from '@adonisjs/core/helpers';
|
|
12
|
-
import * as errors from '../auth/errors.js';
|
|
13
12
|
/**
|
|
14
13
|
* A token represents an opaque token issued to a client
|
|
15
14
|
* to perform a specific task.
|
|
@@ -93,16 +92,19 @@ export class Token {
|
|
|
93
92
|
/**
|
|
94
93
|
* Decodes a publicly shared token and return the series
|
|
95
94
|
* and the token value from it.
|
|
95
|
+
*
|
|
96
|
+
* Returns null when unable to decode the token because of
|
|
97
|
+
* invalid format or encoding.
|
|
96
98
|
*/
|
|
97
99
|
static decode(value) {
|
|
98
100
|
const [series, ...tokenValue] = value.split('.');
|
|
99
101
|
if (!series || tokenValue.length === 0) {
|
|
100
|
-
|
|
102
|
+
return null;
|
|
101
103
|
}
|
|
102
104
|
const decodedSeries = base64.urlDecode(series);
|
|
103
105
|
const decodedValue = base64.urlDecode(tokenValue.join('.'));
|
|
104
106
|
if (!decodedSeries || !decodedValue) {
|
|
105
|
-
|
|
107
|
+
return null;
|
|
106
108
|
}
|
|
107
109
|
return {
|
|
108
110
|
series: decodedSeries,
|
|
@@ -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
|
|
@@ -73,6 +77,22 @@ export declare class SessionGuard<UserProvider extends SessionUserProviderContra
|
|
|
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): 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): Promise<UserProvider[typeof PROVIDER_REAL_USER]>;
|
|
76
96
|
/**
|
|
77
97
|
* Login a user using the user object.
|
|
78
98
|
*/
|
|
@@ -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,7 +120,7 @@ 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) {
|
|
@@ -127,6 +131,18 @@ export class SessionGuard {
|
|
|
127
131
|
}
|
|
128
132
|
throw error;
|
|
129
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Notifies about login failure and throws the exception
|
|
136
|
+
*/
|
|
137
|
+
#loginFailed(error, user) {
|
|
138
|
+
if (this.#emitter) {
|
|
139
|
+
this.#emitter.emit('session_auth:login_failed', {
|
|
140
|
+
error,
|
|
141
|
+
user,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
130
146
|
/**
|
|
131
147
|
* Register the remember me tokens provider to create
|
|
132
148
|
* remember me tokens during user login.
|
|
@@ -153,10 +169,66 @@ export class SessionGuard {
|
|
|
153
169
|
*/
|
|
154
170
|
getUserOrFail() {
|
|
155
171
|
if (!this.user) {
|
|
156
|
-
throw
|
|
172
|
+
throw AuthenticationException.E_INVALID_AUTH_SESSION();
|
|
157
173
|
}
|
|
158
174
|
return this.user;
|
|
159
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Verifies user credentials and returns an instance of
|
|
178
|
+
* the user or throws "E_INVALID_CREDENTIALS" exception.
|
|
179
|
+
*/
|
|
180
|
+
async verifyCredentials(uid, password) {
|
|
181
|
+
debug('session_guard: attempting to verify credentials for uid "%s"', uid);
|
|
182
|
+
/**
|
|
183
|
+
* Attempt to find a user by the uid and raise
|
|
184
|
+
* error when unable to find one
|
|
185
|
+
*/
|
|
186
|
+
const providerUser = await this.#userProvider.findByUid(uid);
|
|
187
|
+
if (!providerUser) {
|
|
188
|
+
this.#loginFailed(InvalidCredentialsException.E_INVALID_CREDENTIALS(this.driverName), null);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Raise error when unable to verify password
|
|
192
|
+
*/
|
|
193
|
+
const user = providerUser.getOriginal();
|
|
194
|
+
/**
|
|
195
|
+
* Raise error when unable to verify password
|
|
196
|
+
*/
|
|
197
|
+
if (!(await providerUser.verifyPassword(password))) {
|
|
198
|
+
this.#loginFailed(InvalidCredentialsException.E_INVALID_CREDENTIALS(this.driverName), user);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Notify credentials have been verified
|
|
202
|
+
*/
|
|
203
|
+
if (this.#emitter) {
|
|
204
|
+
this.#emitter.emit('session_auth:credentials_verified', {
|
|
205
|
+
uid,
|
|
206
|
+
user,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
return user;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Attempt to login a user after verifying their
|
|
213
|
+
* credentials.
|
|
214
|
+
*/
|
|
215
|
+
async attempt(uid, password) {
|
|
216
|
+
const user = await this.verifyCredentials(uid, password);
|
|
217
|
+
return this.login(user);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Attempt to login a user using the user id. The
|
|
221
|
+
* user will be first fetched from the db before
|
|
222
|
+
* marking them as logged-in
|
|
223
|
+
*/
|
|
224
|
+
async loginViaId(id) {
|
|
225
|
+
debug('session_guard: attempting to login user via id "%s"', id);
|
|
226
|
+
const providerUser = await this.#userProvider.findById(id);
|
|
227
|
+
if (!providerUser) {
|
|
228
|
+
this.#loginFailed(InvalidCredentialsException.E_INVALID_CREDENTIALS(this.driverName), null);
|
|
229
|
+
}
|
|
230
|
+
return this.login(providerUser.getOriginal());
|
|
231
|
+
}
|
|
160
232
|
/**
|
|
161
233
|
* Login a user using the user object.
|
|
162
234
|
*/
|
|
@@ -248,7 +320,7 @@ export class SessionGuard {
|
|
|
248
320
|
* storage
|
|
249
321
|
*/
|
|
250
322
|
if (!providerUser) {
|
|
251
|
-
this.#authenticationFailed(
|
|
323
|
+
this.#authenticationFailed(AuthenticationException.E_INVALID_AUTH_SESSION(), session.sessionId);
|
|
252
324
|
}
|
|
253
325
|
this.user = providerUser.getOriginal();
|
|
254
326
|
this.isAuthenticated = true;
|
|
@@ -280,7 +352,7 @@ export class SessionGuard {
|
|
|
280
352
|
*/
|
|
281
353
|
const rememberMeCookie = this.#ctx.request.encryptedCookie(this.rememberMeKeyName);
|
|
282
354
|
if (!rememberMeCookie || !this.#rememberMeTokenProvider) {
|
|
283
|
-
this.#authenticationFailed(
|
|
355
|
+
this.#authenticationFailed(AuthenticationException.E_INVALID_AUTH_SESSION(), session.sessionId);
|
|
284
356
|
}
|
|
285
357
|
debug('session_guard: authenticating user from remember me cookie');
|
|
286
358
|
/**
|
|
@@ -289,9 +361,12 @@ export class SessionGuard {
|
|
|
289
361
|
* is missing or invalid
|
|
290
362
|
*/
|
|
291
363
|
const decodedToken = RememberMeToken.decode(rememberMeCookie);
|
|
364
|
+
if (!decodedToken) {
|
|
365
|
+
this.#authenticationFailed(AuthenticationException.E_INVALID_AUTH_SESSION(), session.sessionId);
|
|
366
|
+
}
|
|
292
367
|
const token = await this.#rememberMeTokenProvider.getTokenBySeries(decodedToken.series);
|
|
293
368
|
if (!token || !token.verify(decodedToken.value)) {
|
|
294
|
-
this.#authenticationFailed(
|
|
369
|
+
this.#authenticationFailed(AuthenticationException.E_INVALID_AUTH_SESSION(), session.sessionId);
|
|
295
370
|
}
|
|
296
371
|
debug('session_guard: found valid remember me token');
|
|
297
372
|
/**
|
|
@@ -300,7 +375,7 @@ export class SessionGuard {
|
|
|
300
375
|
*/
|
|
301
376
|
const providerUser = await this.#userProvider.findById(token.userId);
|
|
302
377
|
if (!providerUser) {
|
|
303
|
-
this.#authenticationFailed(
|
|
378
|
+
this.#authenticationFailed(AuthenticationException.E_INVALID_AUTH_SESSION(), session.sessionId);
|
|
304
379
|
}
|
|
305
380
|
/**
|
|
306
381
|
* Finally, login the user from the remember me token
|
|
@@ -370,8 +445,7 @@ export class SessionGuard {
|
|
|
370
445
|
return true;
|
|
371
446
|
}
|
|
372
447
|
catch (error) {
|
|
373
|
-
if (error instanceof
|
|
374
|
-
error instanceof errors.E_INVALID_AUTH_TOKEN) {
|
|
448
|
+
if (error instanceof AuthenticationException) {
|
|
375
449
|
return false;
|
|
376
450
|
}
|
|
377
451
|
throw error;
|
package/build/stubs/config.stub
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonisjs/auth",
|
|
3
|
-
"version": "9.0.0-
|
|
3
|
+
"version": "9.0.0-1",
|
|
4
4
|
"description": "Official authentication provider for Adonis framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"exports": {
|
|
22
22
|
".": "./build/index.js",
|
|
23
|
-
"./types": "./build/src/types
|
|
23
|
+
"./types": "./build/src/auth/types.js",
|
|
24
24
|
"./auth_provider": "./build/providers/auth_provider.js",
|
|
25
25
|
"./services/main": "./build/services/auth.js",
|
|
26
26
|
"./core/token": "./build/src/core/token.js",
|
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
"./core/token_providers/*": "./build/src/core/token_providers/*.js",
|
|
30
30
|
"./types/core": "./build/src/core/types.js",
|
|
31
31
|
"./session": "./build/src/guards/session/main.js",
|
|
32
|
+
"./initialize_auth_middleware": "./build/src/auth/middleware/initialize_auth_middleware.js",
|
|
33
|
+
"./auth_middleware": "./build/src/auth/middleware/auth_middleware.js",
|
|
32
34
|
"./types/session": "./build/src/guards/session/types.js"
|
|
33
35
|
},
|
|
34
36
|
"scripts": {
|
|
@@ -66,6 +68,7 @@
|
|
|
66
68
|
"@adonisjs/assembler": "^6.1.3-25",
|
|
67
69
|
"@adonisjs/core": "^6.1.5-30",
|
|
68
70
|
"@adonisjs/eslint-config": "^1.1.8",
|
|
71
|
+
"@adonisjs/i18n": "^2.0.0-6",
|
|
69
72
|
"@adonisjs/lucid": "^19.0.0-3",
|
|
70
73
|
"@adonisjs/prettier-config": "^1.1.8",
|
|
71
74
|
"@adonisjs/session": "^7.0.0-13",
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { HttpContext } from '@adonisjs/core/http'
|
|
2
|
-
import { NextFn } from '@adonisjs/core/http/types'
|
|
3
|
-
|
|
4
|
-
type AuthMiddlewareOptions = {
|
|
5
|
-
guards?: (keyof Authenticators)[]
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export default class AuthMiddleware {
|
|
9
|
-
async handle({ auth }: HttpContext, next: NextFn, options?: AuthMiddlewareOptions) {
|
|
10
|
-
return next()
|
|
11
|
-
}
|
|
12
|
-
}
|