@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.
@@ -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;
@@ -16,6 +16,12 @@ export class AuthManager {
16
16
  * Registered guards
17
17
  */
18
18
  #config;
19
+ /**
20
+ * Name of the default guard
21
+ */
22
+ get defaultGuard() {
23
+ return this.#config.default;
24
+ }
19
25
  constructor(config) {
20
26
  this.#config = config;
21
27
  }
@@ -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
- * Invalid token provided
4
+ * Authentication exception is raised when an attempt is
5
+ * made to authenticate an HTTP request
3
6
  */
4
- export declare const E_INVALID_AUTH_TOKEN: new (args?: any, options?: ErrorOptions | undefined) => import("@poppinss/utils").Exception;
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
- * The user session is invalid
46
+ * Invalid credentials exception is raised when unable
47
+ * to verify user credentials during login
7
48
  */
8
- export declare const E_INVALID_AUTH_SESSION: new (args?: any, options?: ErrorOptions | undefined) => import("@poppinss/utils").Exception;
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
+ }
@@ -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 { createError } from '@poppinss/utils';
9
+ import { Exception } from '@poppinss/utils';
10
10
  /**
11
- * Invalid token provided
11
+ * Authentication exception is raised when an attempt is
12
+ * made to authenticate an HTTP request
12
13
  */
13
- export const E_INVALID_AUTH_TOKEN = createError('Invalid or expired token value', 'E_INVALID_AUTH_TOKEN', 401);
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
- * The user session is invalid
100
+ * Invalid credentials exception is raised when unable
101
+ * to verify user credentials during login
16
102
  */
17
- export const E_INVALID_AUTH_SESSION = createError('Invalid or expired authentication session', 'E_INVALID_AUTH_SESSION', 401);
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,6 @@
1
+ export default class AuthMiddleware {
2
+ async handle(ctx, next, options = {}) {
3
+ await ctx.auth.authenticateUsing(options.guards, options);
4
+ return next();
5
+ }
6
+ }
@@ -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 type
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<unknown>> = Awaited<ReturnType<Config['resolver']>>;
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
  };
@@ -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
- throw new errors.E_INVALID_AUTH_TOKEN();
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
- throw new errors.E_INVALID_AUTH_TOKEN();
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 authenticatin failure and throws the exception
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 new errors.E_INVALID_AUTH_SESSION();
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(new errors.E_INVALID_AUTH_SESSION(), session.sessionId);
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(new errors.E_INVALID_AUTH_SESSION(), session.sessionId);
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(new errors.E_INVALID_AUTH_SESSION(), session.sessionId);
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(new errors.E_INVALID_AUTH_SESSION(), session.sessionId);
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 errors.E_INVALID_AUTH_SESSION ||
374
- error instanceof errors.E_INVALID_AUTH_TOKEN) {
448
+ if (error instanceof AuthenticationException) {
375
449
  return false;
376
450
  }
377
451
  throw error;
@@ -34,7 +34,6 @@ export type SessionGuardEvents<User> = {
34
34
  'session_auth:credentials_verified': {
35
35
  uid: string;
36
36
  user: User;
37
- password: string;
38
37
  };
39
38
  /**
40
39
  * The event is emitted when unable to login the
@@ -21,7 +21,7 @@ const userProvider = providers.db({
21
21
  table: 'users',
22
22
  passwordColumnName: 'password',
23
23
  id: 'id',
24
- uids: ['email']
24
+ uids: ['email'],
25
25
  })
26
26
  {{/if}}
27
27
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonisjs/auth",
3
- "version": "9.0.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/main.js",
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
- }