diff options
Diffstat (limited to 'app/Middleware/Auth.ts')
-rw-r--r-- | app/Middleware/Auth.ts | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/app/Middleware/Auth.ts b/app/Middleware/Auth.ts new file mode 100644 index 0000000..d0b212c --- /dev/null +++ b/app/Middleware/Auth.ts | |||
@@ -0,0 +1,118 @@ | |||
1 | import { GuardsList } from '@ioc:Adonis/Addons/Auth'; | ||
2 | import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; | ||
3 | import { AuthenticationException } from '@adonisjs/auth/build/standalone'; | ||
4 | import * as jose from 'jose'; | ||
5 | import { appKey } from 'Config/app'; | ||
6 | import User from 'App/Models/User'; | ||
7 | |||
8 | /** | ||
9 | * Auth middleware is meant to restrict un-authenticated access to a given route | ||
10 | * or a group of routes. | ||
11 | * | ||
12 | * You must register this middleware inside `start/kernel.ts` file under the list | ||
13 | * of named middleware. | ||
14 | */ | ||
15 | export default class AuthMiddleware { | ||
16 | /** | ||
17 | * The URL to redirect to when request is Unauthorized | ||
18 | */ | ||
19 | protected redirectTo = '/user/login'; | ||
20 | |||
21 | /** | ||
22 | * Authenticates the current HTTP request against a custom set of defined | ||
23 | * guards. | ||
24 | * | ||
25 | * The authentication loop stops as soon as the user is authenticated using any | ||
26 | * of the mentioned guards and that guard will be used by the rest of the code | ||
27 | * during the current request. | ||
28 | */ | ||
29 | protected async authenticate( | ||
30 | auth: HttpContextContract['auth'], | ||
31 | guards: (keyof GuardsList)[], | ||
32 | request: HttpContextContract['request'], | ||
33 | ) { | ||
34 | /** | ||
35 | * Hold reference to the guard last attempted within the for loop. We pass | ||
36 | * the reference of the guard to the "AuthenticationException", so that | ||
37 | * it can decide the correct response behavior based upon the guard | ||
38 | * driver | ||
39 | */ | ||
40 | let guardLastAttempted: string | undefined; | ||
41 | |||
42 | for (const guard of guards) { | ||
43 | guardLastAttempted = guard; | ||
44 | |||
45 | let isLoggedIn = false; | ||
46 | try { | ||
47 | // eslint-disable-next-line no-await-in-loop | ||
48 | isLoggedIn = await auth.use(guard).check(); | ||
49 | } catch { | ||
50 | // Silent fail to allow the rest of the code to handle the error | ||
51 | } | ||
52 | |||
53 | if (isLoggedIn) { | ||
54 | /** | ||
55 | * Instruct auth to use the given guard as the default guard for | ||
56 | * the rest of the request, since the user authenticated | ||
57 | * succeeded here | ||
58 | */ | ||
59 | auth.defaultGuard = guard; | ||
60 | return; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | // Manually try authenticating using the JWT (verfiy signature required) | ||
65 | // Legacy support for JWTs so that the client still works (older than 2.0.0) | ||
66 | const authToken = request.headers().authorization?.split(' ')[1]; | ||
67 | if (authToken) { | ||
68 | try { | ||
69 | const jwt = await jose.jwtVerify( | ||
70 | authToken, | ||
71 | new TextEncoder().encode(appKey), | ||
72 | ); | ||
73 | const { uid } = jwt.payload; | ||
74 | |||
75 | // @ts-expect-error | ||
76 | request.user = await User.findOrFail(uid); | ||
77 | return; | ||
78 | } catch { | ||
79 | // Silent fail to allow the rest of the code to handle the error | ||
80 | } | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * Unable to authenticate using any guard | ||
85 | */ | ||
86 | throw new AuthenticationException( | ||
87 | 'Unauthorized access', | ||
88 | 'E_UNAUTHORIZED_ACCESS', | ||
89 | guardLastAttempted, | ||
90 | this.redirectTo, | ||
91 | ); | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Handle request | ||
96 | */ | ||
97 | public async handle( | ||
98 | { request, auth, response }: HttpContextContract, | ||
99 | next: () => Promise<void>, | ||
100 | customGuards: (keyof GuardsList)[], | ||
101 | ) { | ||
102 | /** | ||
103 | * Uses the user defined guards or the default guard mentioned in | ||
104 | * the config file | ||
105 | */ | ||
106 | const guards = customGuards.length > 0 ? customGuards : [auth.name]; | ||
107 | try { | ||
108 | await this.authenticate(auth, guards, request); | ||
109 | } catch (error) { | ||
110 | // If the user is not authenticated and it is a web endpoint, redirect to the login page | ||
111 | if (guards.includes('web')) { | ||
112 | return response.redirect(error.redirectTo); | ||
113 | } | ||
114 | throw error; | ||
115 | } | ||
116 | await next(); | ||
117 | } | ||
118 | } | ||