@adonisjs/auth 9.0.0-3 → 9.0.0-5

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.
@@ -6,47 +6,20 @@
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 { stubsRoot } from './index.js';
10
- /**
11
- * Configures the user provider to use for finding
12
- * users
13
- */
14
- async function configureProvider(command) {
15
- const provider = await command.prompt.choice('Select the user provider you want to use', [
16
- {
17
- name: 'lucid',
18
- message: 'Lucid models',
19
- },
20
- {
21
- name: 'db',
22
- message: 'Database query builder',
23
- },
24
- ]);
25
- /**
26
- * Publish config file
27
- */
28
- await command.publishStub('config.stub', { provider });
29
- }
30
- /**
31
- * Configures the session guard and output its config
32
- * to the console
33
- */
34
- async function configureSessionGuard(command) {
35
- const tokens = await command.prompt.confirm('Do you want to use remember me tokens?');
36
- const stubs = await command.app.stubs.create();
37
- const stub = await stubs.build('guards/session.stub', { source: stubsRoot });
38
- const { contents } = await stub.prepare({ tokens });
39
- command.logger.log(contents);
40
- }
41
9
  /**
42
10
  * Configures the auth package
43
11
  */
44
12
  export async function configure(command) {
45
- if (command.parsedFlags && command.parsedFlags.guard === 'session') {
46
- return configureSessionGuard(command);
47
- }
48
- await configureProvider(command);
49
13
  const codemods = await command.createCodemods();
14
+ /**
15
+ * Publish middleware to user application
16
+ */
17
+ await command.publishStub('middleware/auth_middleware.stub', {
18
+ entity: command.app.generators.createEntity('auth'),
19
+ });
20
+ await command.publishStub('middleware/guest_middleware.stub', {
21
+ entity: command.app.generators.createEntity('guest'),
22
+ });
50
23
  /**
51
24
  * Register provider
52
25
  */
@@ -64,7 +37,11 @@ export async function configure(command) {
64
37
  await codemods.registerMiddleware('named', [
65
38
  {
66
39
  name: 'auth',
67
- path: '@adonisjs/auth/auth_middleware',
40
+ path: '#middleware/auth_middleware',
41
+ },
42
+ {
43
+ name: 'guest',
44
+ path: '#middleware/guest_middleware',
68
45
  },
69
46
  ]);
70
47
  }
package/build/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { configure } from './configure.js';
2
2
  export { stubsRoot } from './stubs/main.js';
3
- export * as errors from './src/auth/errors.js';
4
3
  export * as symbols from './src/auth/symbols.js';
5
4
  export { AuthManager } from './src/auth/auth_manager.js';
6
5
  export { Authenticator } from './src/auth/authenticator.js';
7
6
  export { defineConfig, providers } from './src/auth/define_config.js';
7
+ export { AuthenticationException, InvalidCredentialsException } from './src/auth/errors.js';
package/build/index.js CHANGED
@@ -8,8 +8,8 @@
8
8
  */
9
9
  export { configure } from './configure.js';
10
10
  export { stubsRoot } from './stubs/main.js';
11
- export * as errors from './src/auth/errors.js';
12
11
  export * as symbols from './src/auth/symbols.js';
13
12
  export { AuthManager } from './src/auth/auth_manager.js';
14
13
  export { Authenticator } from './src/auth/authenticator.js';
15
14
  export { defineConfig, providers } from './src/auth/define_config.js';
15
+ export { AuthenticationException, InvalidCredentialsException } from './src/auth/errors.js';
@@ -13,7 +13,6 @@ export declare class AuthManager<KnownGuards extends Record<string, GuardFactory
13
13
  get defaultGuard(): keyof KnownGuards;
14
14
  constructor(config: {
15
15
  default: keyof KnownGuards;
16
- loginRoute: string;
17
16
  guards: KnownGuards;
18
17
  });
19
18
  /**
@@ -33,9 +33,15 @@ export declare class Authenticator<KnownGuards extends Record<string, GuardFacto
33
33
  get authenticationAttempted(): boolean;
34
34
  constructor(ctx: HttpContext, config: {
35
35
  default: keyof KnownGuards;
36
- loginRoute: string;
37
36
  guards: KnownGuards;
38
37
  });
38
+ /**
39
+ * Returns an instance of the logged-in user or throws an
40
+ * exception
41
+ */
42
+ getUserOrFail(): {
43
+ [K in keyof KnownGuards]: ReturnType<ReturnType<KnownGuards[K]>['getUserOrFail']>;
44
+ }[keyof KnownGuards];
39
45
  /**
40
46
  * Returns an instance of a known guard. Guards instances are
41
47
  * cached during the lifecycle of an HTTP request.
@@ -68,6 +68,13 @@ export class Authenticator {
68
68
  this.#config = config;
69
69
  debug('creating authenticator. config %O', this.#config);
70
70
  }
71
+ /**
72
+ * Returns an instance of the logged-in user or throws an
73
+ * exception
74
+ */
75
+ getUserOrFail() {
76
+ return this.use(this.#authenticatedViaGuard || this.defaultGuard).getUserOrFail();
77
+ }
71
78
  /**
72
79
  * Returns an instance of a known guard. Guards instances are
73
80
  * cached during the lifecycle of an HTTP request.
@@ -116,7 +123,7 @@ export class Authenticator {
116
123
  throw new AuthenticationException('Unauthorized access', {
117
124
  code: 'E_UNAUTHORIZED_ACCESS',
118
125
  guardDriverName: lastUsedGuardDriver,
119
- redirectTo: options?.loginRoute || this.#config.loginRoute,
126
+ redirectTo: options?.loginRoute,
120
127
  });
121
128
  }
122
129
  }
@@ -5,6 +5,8 @@ import { HttpContext } from '@adonisjs/core/http';
5
5
  * made to authenticate an HTTP request
6
6
  */
7
7
  export declare class AuthenticationException extends Exception {
8
+ static status?: number | undefined;
9
+ static code?: string | undefined;
8
10
  /**
9
11
  * Raises authentication exception when session guard
10
12
  * is unable to authenticate the request
@@ -49,6 +51,7 @@ export declare class AuthenticationException extends Exception {
49
51
  export declare class InvalidCredentialsException extends Exception {
50
52
  static message: string;
51
53
  static code: string;
54
+ static status?: number | undefined;
52
55
  static E_INVALID_CREDENTIALS(guardDriverName: string): InvalidCredentialsException;
53
56
  guardDriverName: string;
54
57
  identifier: string;
@@ -12,6 +12,8 @@ import { Exception } from '@poppinss/utils';
12
12
  * made to authenticate an HTTP request
13
13
  */
14
14
  export class AuthenticationException extends Exception {
15
+ static status = 401;
16
+ static code = 'E_UNAUTHORIZED_ACCESS';
15
17
  /**
16
18
  * Raises authentication exception when session guard
17
19
  * is unable to authenticate the request
@@ -103,6 +105,7 @@ export class AuthenticationException extends Exception {
103
105
  export class InvalidCredentialsException extends Exception {
104
106
  static message = 'Invalid credentials';
105
107
  static code = 'E_INVALID_CREDENTIALS';
108
+ static status = 400;
106
109
  static E_INVALID_CREDENTIALS(guardDriverName) {
107
110
  return new InvalidCredentialsException(InvalidCredentialsException.message, {
108
111
  guardDriverName,
@@ -10,6 +10,10 @@ export interface GuardContract<User> {
10
10
  * Reference to the currently authenticated user
11
11
  */
12
12
  user?: User;
13
+ /**
14
+ * Returns logged-in user or throws an exception
15
+ */
16
+ getUserOrFail(): User;
13
17
  /**
14
18
  * A boolean to know if the current request has
15
19
  * been authenticated
@@ -60,6 +64,16 @@ export type InferAuthenticators<Config extends ConfigProvider<{
60
64
  default: unknown;
61
65
  guards: unknown;
62
66
  }>> = Awaited<ReturnType<Config['resolver']>>['guards'];
67
+ /**
68
+ * Helper to convert union to intersection
69
+ */
70
+ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
71
+ /**
72
+ * Infer events based upon the configure authenticators
73
+ */
74
+ export type InferAuthEvents<KnownAuthenticators extends Record<string, GuardFactory>> = UnionToIntersection<{
75
+ [K in keyof KnownAuthenticators]: ReturnType<KnownAuthenticators[K]>[typeof GUARD_KNOWN_EVENTS];
76
+ }[keyof KnownAuthenticators]>;
63
77
  /**
64
78
  * Auth service is a singleton instance of the AuthManager
65
79
  * configured using the config stored within the user
@@ -73,3 +87,4 @@ export interface AuthService extends AuthManager<Authenticators extends Record<s
73
87
  export type GuardConfigProvider<Guard extends GuardFactory> = {
74
88
  resolver: (name: string, app: ApplicationService) => Promise<Guard>;
75
89
  };
90
+ export {};
@@ -11,7 +11,7 @@ type DatabaseTokenRow = {
11
11
  created_at: Date;
12
12
  updated_at: Date;
13
13
  expires_at: Date | null;
14
- };
14
+ } & Record<string, any>;
15
15
  /**
16
16
  * A generic implementation to read tokens from the database
17
17
  */
@@ -86,13 +86,13 @@ export declare class SessionGuard<UserProvider extends SessionUserProviderContra
86
86
  * Attempt to login a user after verifying their
87
87
  * credentials.
88
88
  */
89
- attempt(uid: string, password: string): Promise<UserProvider[typeof PROVIDER_REAL_USER]>;
89
+ attempt(uid: string, password: string, remember?: boolean): Promise<UserProvider[typeof PROVIDER_REAL_USER]>;
90
90
  /**
91
91
  * Attempt to login a user using the user id. The
92
92
  * user will be first fetched from the db before
93
93
  * marking them as logged-in
94
94
  */
95
- loginViaId(id: string | number): Promise<UserProvider[typeof PROVIDER_REAL_USER]>;
95
+ loginViaId(id: string | number, remember?: boolean): Promise<UserProvider[typeof PROVIDER_REAL_USER]>;
96
96
  /**
97
97
  * Login a user using the user object.
98
98
  */
@@ -109,4 +109,8 @@ export declare class SessionGuard<UserProvider extends SessionUserProviderContra
109
109
  * succeeded or failed.
110
110
  */
111
111
  check(): Promise<boolean>;
112
+ /**
113
+ * Logout user and revoke remember me token (if any)
114
+ */
115
+ logout(): Promise<void>;
112
116
  }
@@ -125,6 +125,7 @@ export class SessionGuard {
125
125
  #authenticationFailed(error, sessionId) {
126
126
  if (this.#emitter) {
127
127
  this.#emitter.emit('session_auth:authentication_failed', {
128
+ guardName: this.#name,
128
129
  error,
129
130
  sessionId: sessionId,
130
131
  });
@@ -137,6 +138,7 @@ export class SessionGuard {
137
138
  #loginFailed(error, user) {
138
139
  if (this.#emitter) {
139
140
  this.#emitter.emit('session_auth:login_failed', {
141
+ guardName: this.#name,
140
142
  error,
141
143
  user,
142
144
  });
@@ -202,6 +204,7 @@ export class SessionGuard {
202
204
  */
203
205
  if (this.#emitter) {
204
206
  this.#emitter.emit('session_auth:credentials_verified', {
207
+ guardName: this.#name,
205
208
  uid,
206
209
  user,
207
210
  });
@@ -212,29 +215,29 @@ export class SessionGuard {
212
215
  * Attempt to login a user after verifying their
213
216
  * credentials.
214
217
  */
215
- async attempt(uid, password) {
218
+ async attempt(uid, password, remember) {
216
219
  const user = await this.verifyCredentials(uid, password);
217
- return this.login(user);
220
+ return this.login(user, remember);
218
221
  }
219
222
  /**
220
223
  * Attempt to login a user using the user id. The
221
224
  * user will be first fetched from the db before
222
225
  * marking them as logged-in
223
226
  */
224
- async loginViaId(id) {
227
+ async loginViaId(id, remember) {
225
228
  debug('session_guard: attempting to login user via id "%s"', id);
226
229
  const providerUser = await this.#userProvider.findById(id);
227
230
  if (!providerUser) {
228
231
  this.#loginFailed(InvalidCredentialsException.E_INVALID_CREDENTIALS(this.driverName), null);
229
232
  }
230
- return this.login(providerUser.getOriginal());
233
+ return this.login(providerUser.getOriginal(), remember);
231
234
  }
232
235
  /**
233
236
  * Login a user using the user object.
234
237
  */
235
238
  async login(user, remember = false) {
236
239
  if (this.#emitter) {
237
- this.#emitter.emit('session_auth:login_attempted', { user });
240
+ this.#emitter.emit('session_auth:login_attempted', { user, guardName: this.#name });
238
241
  }
239
242
  const providerUser = await this.#userProvider.createUserForGuard(user);
240
243
  const session = this.#getSession();
@@ -281,6 +284,7 @@ export class SessionGuard {
281
284
  */
282
285
  if (this.#emitter) {
283
286
  this.#emitter.emit('session_auth:login_succeeded', {
287
+ guardName: this.#name,
284
288
  user,
285
289
  sessionId: session.sessionId,
286
290
  rememberMeToken: token,
@@ -303,6 +307,7 @@ export class SessionGuard {
303
307
  */
304
308
  if (this.#emitter) {
305
309
  this.#emitter.emit('session_auth:authentication_attempted', {
310
+ guardName: this.#name,
306
311
  sessionId: session.sessionId,
307
312
  });
308
313
  }
@@ -331,6 +336,7 @@ export class SessionGuard {
331
336
  */
332
337
  if (this.#emitter) {
333
338
  this.#emitter.emit('session_auth:authentication_succeeded', {
339
+ guardName: this.#name,
334
340
  sessionId: session.sessionId,
335
341
  user: this.user,
336
342
  });
@@ -393,6 +399,7 @@ export class SessionGuard {
393
399
  */
394
400
  if (this.#emitter) {
395
401
  this.#emitter.emit('session_auth:authentication_succeeded', {
402
+ guardName: this.#name,
396
403
  sessionId: session.sessionId,
397
404
  user: this.user,
398
405
  rememberMeToken: token,
@@ -451,4 +458,37 @@ export class SessionGuard {
451
458
  throw error;
452
459
  }
453
460
  }
461
+ /**
462
+ * Logout user and revoke remember me token (if any)
463
+ */
464
+ async logout() {
465
+ debug('session_auth: logging out');
466
+ const session = this.#getSession();
467
+ /**
468
+ * Clear client side state
469
+ */
470
+ session.forget(this.sessionKeyName);
471
+ this.#ctx.response.clearCookie(this.rememberMeKeyName);
472
+ /**
473
+ * Notify the user has been logged out
474
+ */
475
+ if (this.#emitter) {
476
+ this.#emitter.emit('session_auth:logged_out', {
477
+ guardName: this.#name,
478
+ user: this.user || null,
479
+ sessionId: session.sessionId,
480
+ });
481
+ }
482
+ const rememberMeCookie = this.#ctx.request.encryptedCookie(this.rememberMeKeyName);
483
+ if (!rememberMeCookie || !this.#rememberMeTokenProvider) {
484
+ return;
485
+ }
486
+ debug('session_auth: decoding remember me token');
487
+ const decodedToken = RememberMeToken.decode(rememberMeCookie);
488
+ if (!decodedToken) {
489
+ return;
490
+ }
491
+ debug('session_auth: deleting remember me token');
492
+ await this.#rememberMeTokenProvider.deleteTokenBySeries(decodedToken.series);
493
+ }
454
494
  }
@@ -1,4 +1,4 @@
1
- import { Exception } from '@poppinss/utils';
1
+ import type { Exception } from '@poppinss/utils';
2
2
  import type { RememberMeToken } from './token.js';
3
3
  import type { UserProviderContract, TokenProviderContract, DatabaseTokenProviderOptions } from '../../core/types.js';
4
4
  /**
@@ -32,6 +32,7 @@ export type SessionGuardEvents<User> = {
32
32
  * have been verified successfully.
33
33
  */
34
34
  'session_auth:credentials_verified': {
35
+ guardName: string;
35
36
  uid: string;
36
37
  user: User;
37
38
  };
@@ -40,6 +41,7 @@ export type SessionGuardEvents<User> = {
40
41
  * user.
41
42
  */
42
43
  'session_auth:login_failed': {
44
+ guardName: string;
43
45
  error: Exception;
44
46
  user: User | null;
45
47
  };
@@ -48,6 +50,7 @@ export type SessionGuardEvents<User> = {
48
50
  * a given user.
49
51
  */
50
52
  'session_auth:login_attempted': {
53
+ guardName: string;
51
54
  user: User;
52
55
  };
53
56
  /**
@@ -55,6 +58,7 @@ export type SessionGuardEvents<User> = {
55
58
  * successfully
56
59
  */
57
60
  'session_auth:login_succeeded': {
61
+ guardName: string;
58
62
  user: User;
59
63
  sessionId: string;
60
64
  rememberMeToken?: RememberMeToken;
@@ -63,12 +67,14 @@ export type SessionGuardEvents<User> = {
63
67
  * Attempting to authenticate the user
64
68
  */
65
69
  'session_auth:authentication_attempted': {
70
+ guardName: string;
66
71
  sessionId: string;
67
72
  };
68
73
  /**
69
74
  * Authentication was successful
70
75
  */
71
76
  'session_auth:authentication_succeeded': {
77
+ guardName: string;
72
78
  user: User;
73
79
  sessionId: string;
74
80
  rememberMeToken?: RememberMeToken;
@@ -77,6 +83,7 @@ export type SessionGuardEvents<User> = {
77
83
  * Authentication failed
78
84
  */
79
85
  'session_auth:authentication_failed': {
86
+ guardName: string;
80
87
  error: Exception;
81
88
  sessionId: string;
82
89
  };
@@ -85,7 +92,8 @@ export type SessionGuardEvents<User> = {
85
92
  * sucessfully
86
93
  */
87
94
  'session_auth:logged_out': {
88
- user: User;
95
+ guardName: string;
96
+ user: User | null;
89
97
  sessionId: string;
90
98
  };
91
99
  };
@@ -0,0 +1,30 @@
1
+ {{#var middlewareName = generators.middlewareName(entity.name)}}
2
+ {{#var middlewareFileName = generators.middlewareFileName(entity.name)}}
3
+ {{{
4
+ exports({ to: app.middlewarePath(entity.path, middlewareFileName) })
5
+ }}}
6
+ import type { HttpContext } from '@adonisjs/core/http'
7
+ import type { NextFn } from '@adonisjs/core/types/http'
8
+ import type { Authenticators } from '@adonisjs/auth/types'
9
+
10
+ /**
11
+ * Auth middleware is used authenticate HTTP requests and deny
12
+ * access to unauthenticated users.
13
+ */
14
+ export default class {{ middlewareName }} {
15
+ /**
16
+ * The URL to redirect to, when authentication fails
17
+ */
18
+ redirectTo = '/login'
19
+
20
+ async handle(
21
+ ctx: HttpContext,
22
+ next: NextFn,
23
+ options: {
24
+ guards?: (keyof Authenticators)[]
25
+ } = {}
26
+ ) {
27
+ await ctx.auth.authenticateUsing(options.guards, { loginRoute: this.redirectTo })
28
+ return next()
29
+ }
30
+ }
@@ -0,0 +1,36 @@
1
+ {{#var middlewareName = generators.middlewareName(entity.name)}}
2
+ {{#var middlewareFileName = generators.middlewareFileName(entity.name)}}
3
+ {{{
4
+ exports({ to: app.middlewarePath(entity.path, middlewareFileName) })
5
+ }}}
6
+ import type { HttpContext } from '@adonisjs/core/http'
7
+ import type { NextFn } from '@adonisjs/core/types/http'
8
+ import type { Authenticators } from '@adonisjs/auth/types'
9
+
10
+ /**
11
+ * Guest middleware is used to deny access to routes that should
12
+ * be accessed by unauthenticated users.
13
+ *
14
+ * For example, the login page should not be accessible if the user
15
+ * is already logged-in
16
+ */
17
+ export default class {{ middlewareName }} {
18
+ /**
19
+ * The URL to redirect to when user is logged-in
20
+ */
21
+ redirectTo = '/'
22
+
23
+ async handle(
24
+ ctx: HttpContext,
25
+ next: NextFn,
26
+ options: { guards?: (keyof Authenticators)[] } = {}
27
+ ) {
28
+ for (let guard of options.guards || [ctx.auth.defaultGuard]) {
29
+ if (await ctx.auth.use(guard).check()) {
30
+ return ctx.response.redirect(this.redirectTo, true)
31
+ }
32
+ }
33
+
34
+ return next()
35
+ }
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonisjs/auth",
3
- "version": "9.0.0-3",
3
+ "version": "9.0.0-5",
4
4
  "description": "Official authentication provider for Adonis framework",
5
5
  "type": "module",
6
6
  "main": "build/index.js",
@@ -30,7 +30,6 @@
30
30
  "./types/core": "./build/src/core/types.js",
31
31
  "./session": "./build/src/guards/session/main.js",
32
32
  "./initialize_auth_middleware": "./build/src/auth/middleware/initialize_auth_middleware.js",
33
- "./auth_middleware": "./build/src/auth/middleware/auth_middleware.js",
34
33
  "./types/session": "./build/src/guards/session/types.js"
35
34
  },
36
35
  "scripts": {
@@ -1,13 +0,0 @@
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
- loginRoute?: string;
10
- };
11
- export default class AuthMiddleware {
12
- handle(ctx: HttpContext, next: NextFn, options?: AuthMiddlewareOptions): Promise<any>;
13
- }
@@ -1,6 +0,0 @@
1
- export default class AuthMiddleware {
2
- async handle(ctx, next, options = {}) {
3
- await ctx.auth.authenticateUsing(options.guards, options);
4
- return next();
5
- }
6
- }
@@ -1,39 +0,0 @@
1
- {{{
2
- exports({ to: app.configPath('auth.ts') })
3
- }}}
4
- import { defineConfig, providers } from '@adonisjs/auth'
5
- {{#if provider === 'lucid'}}
6
- /**
7
- * Using the "models/user" model to find users during
8
- * login and authentication
9
- */
10
- const userProvider = providers.lucid({
11
- model: () => import('#models/user'),
12
- uids: ['email'],
13
- })
14
- {{/if}}
15
- {{#if provider === 'db'}}
16
- /**
17
- * Using Lucid query builder to directly query the database
18
- * to find users during login and authentication.
19
- */
20
- const userProvider = providers.db({
21
- table: 'users',
22
- passwordColumnName: 'password',
23
- id: 'id',
24
- uids: ['email'],
25
- })
26
- {{/if}}
27
-
28
- const authConfig = defineConfig({
29
- default: 'web',
30
- loginRoute: '/login',
31
- guards: {
32
- web: {} // to be configured
33
- }
34
- })
35
-
36
- export default authConfig
37
- declare module '@adonisjs/auth/types' {
38
- interface Authenticators extends InferAuthenticators<typeof authConfig> {}
39
- }
@@ -1,21 +0,0 @@
1
- {{{
2
- exports({
3
- to: app.makePath('config/auth.ts')
4
- })
5
- }}}
6
- {{#if tokens}}
7
- import { sessionGuard, tokensProvider } from '@adonisjs/auth/session'
8
- {{#else}}
9
- import { sessionGuard } from '@adonisjs/auth/session'
10
- {{/if}}
11
-
12
- {
13
- web: sessionGuard({
14
- provider: userProvider,
15
- {{#if tokens}}
16
- tokens: tokensProvider.db({
17
- table: 'remember_me_tokens'
18
- })
19
- {{/if}}
20
- })
21
- }