Skip to content

Commit 7a4d66a

Browse files
authored
feat: EXP-1045 Auth -> JwtAuthenticator access to request object (#787)
* EXP-1045 JwtBasedAuthenticator passing request * EXP-1045 Update name for consistency * EXP-1045 Fixing tests * Readme update * readme issue fix
1 parent 39a687e commit 7a4d66a

File tree

6 files changed

+41
-30
lines changed

6 files changed

+41
-30
lines changed

packages/app/auth/README.md

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ import {
2525
type BaseAuthInfo
2626
} from '@lokalise/auth'
2727

28+
// Define your JWT payload type
29+
type MyJwtPayload = {
30+
userId: string
31+
email: string
32+
}
33+
2834
// Define your authentication info type
2935
type MyAuthInfo = BaseAuthInfo<'my-provider'> & {
3036
userId: string
@@ -33,19 +39,21 @@ type MyAuthInfo = BaseAuthInfo<'my-provider'> & {
3339

3440
// Create a custom authenticator
3541
class MyAuthenticator extends JwtBasedAuthenticator<MyAuthInfo> {
36-
protected internalAuthenticate(reqContext, jwtPayload, rawToken) {
42+
protected internalAuthenticate(reqContext, jwt, request) {
43+
const payload = jwt.payload as MyJwtPayload
44+
3745
// Validate the JWT payload and extract user information
38-
if (!jwtPayload.sub || !jwtPayload.email) {
46+
if (!payload.userId || !payload.email) {
3947
return { success: false, failure: 'INVALID_CREDENTIALS' }
4048
}
4149

4250
return {
4351
success: true,
4452
authInfo: {
4553
authType: 'my-provider',
46-
rawToken,
47-
userId: jwtPayload.sub,
48-
email: jwtPayload.email
54+
token: jwt.token,
55+
userId: payload.userId,
56+
email: payload.email
4957
}
5058
}
5159
}
@@ -118,7 +126,7 @@ Base interface for authentication information:
118126
```typescript
119127
type BaseAuthInfo<AuthType extends string> = {
120128
authType: AuthType
121-
rawToken: string
129+
token: string
122130
}
123131
```
124132
@@ -150,13 +158,11 @@ Abstract base class for JWT-based authentication:
150158

151159
```typescript
152160
abstract class JwtBasedAuthenticator<AuthInfo extends BaseAuthInfo<string>> {
153-
constructor(tokenDecoder: TokenDecoder, tokenHeader?: string)
154-
155161
// Must be implemented by subclasses
156162
protected abstract internalAuthenticate(
157163
reqContext: RequestContext,
158-
jwtPayload: object,
159-
rawToken: string
164+
jwt: ValidatedJwt,
165+
request: FastifyRequest
160166
): AuthResult<AuthInfo> | Promise<AuthResult<AuthInfo>>
161167
}
162168
```
@@ -165,11 +171,6 @@ abstract class JwtBasedAuthenticator<AuthInfo extends BaseAuthInfo<string>> {
165171

166172
Chains multiple authenticators, trying each until one succeeds:
167173

168-
```typescript
169-
class AuthenticatorChain<AuthInfo extends BaseAuthInfo<string>> {
170-
constructor(authentators: Authenticator<AuthInfo>[])
171-
}
172-
```
173174

174175
### Token Decoders
175176

packages/app/auth/src/authenticators/Authenticator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type { FastifyRequest } from 'fastify'
66
export type BaseAuthInfo<AuthType extends string> = {
77
authType: AuthType
88
/** The raw authentication token. May be forwarded to downstream services. */
9-
rawToken: string
9+
token: string
1010
}
1111

1212
/**

packages/app/auth/src/authenticators/AuthenticatorChain.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describe('AuthenticatorChain', () => {
3434
// Given
3535
const authInfo: AuthInfo = {
3636
authType: 'provider-1',
37-
rawToken: 'raw-token',
37+
token: 'raw-token',
3838
}
3939

4040
const firstAuth = new MockAuthenticator({ success: true, authInfo })
@@ -53,7 +53,7 @@ describe('AuthenticatorChain', () => {
5353
// Given
5454
const authInfo: AuthInfo = {
5555
authType: 'provider-2',
56-
rawToken: 'raw-token',
56+
token: 'raw-token',
5757
}
5858

5959
const firstAuth = new MockAuthenticator({ success: false, failure: 'INVALID_CREDENTIALS' })

packages/app/auth/src/authenticators/JwtBasedAuthenticator.spec.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { RequestContext } from '@lokalise/fastify-extras'
2+
import type { FastifyRequest } from 'fastify'
23
import { beforeEach, describe, expect, it, vi } from 'vitest'
34
import { createMockFastifyRequest } from '../../tests/createMockFastifyRequest.ts'
45
import { createToken } from '../../tests/createToken.ts'
@@ -9,7 +10,7 @@ import {
910
type TokenValidationError,
1011
} from '../token-decoders/index.ts'
1112
import type { AuthFailureReason, AuthResult, BaseAuthInfo } from './Authenticator.ts'
12-
import { JwtBasedAuthenticator } from './JwtBasedAuthenticator.ts'
13+
import { JwtBasedAuthenticator, type ValidatedJwt } from './JwtBasedAuthenticator.ts'
1314

1415
type AuthInfo = BaseAuthInfo<'test-provider'>
1516

@@ -23,16 +24,18 @@ class TestJwtAuthenticator extends JwtBasedAuthenticator<AuthInfo> {
2324

2425
protected override internalAuthenticate(
2526
_reqContext: RequestContext,
26-
_jwtPayload: object,
27-
rawToken: string,
27+
jwt: ValidatedJwt,
28+
request: FastifyRequest,
2829
): AuthResult<AuthInfo> | Promise<AuthResult<AuthInfo>> {
2930
if (!this.error) {
3031
return {
3132
success: true,
32-
authInfo: { authType: 'test-provider', rawToken },
33+
authInfo: { authType: 'test-provider', token: jwt.token },
3334
}
3435
}
3536

37+
expect(request).toBeDefined()
38+
3639
return { success: false, failure: this.error }
3740
}
3841
}
@@ -60,7 +63,7 @@ describe('JwtBasedAuthenticator', () => {
6063
// Then
6164
expect(result).toEqual({
6265
success: true,
63-
authInfo: { authType: 'test-provider', rawToken: token },
66+
authInfo: { authType: 'test-provider', token: token },
6467
})
6568
})
6669

@@ -80,7 +83,7 @@ describe('JwtBasedAuthenticator', () => {
8083
// Then
8184
expect(result).toEqual({
8285
success: true,
83-
authInfo: { authType: 'test-provider', rawToken: token },
86+
authInfo: { authType: 'test-provider', token: token },
8487
})
8588
})
8689
})

packages/app/auth/src/authenticators/JwtBasedAuthenticator.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ import type { FastifyRequest } from 'fastify'
33
import type { TokenDecoder } from '../token-decoders/index.ts'
44
import type { Authenticator, AuthResult, BaseAuthInfo } from './Authenticator.ts'
55

6+
export type ValidatedJwt = {
7+
payload: object
8+
token: string
9+
}
10+
611
/**
712
* Abstract base class for JWT-based authenticators.
813
*/
@@ -27,7 +32,9 @@ export abstract class JwtBasedAuthenticator<AuthInfo extends BaseAuthInfo<string
2732
}
2833

2934
return this.tokenDecoder.decode(request.reqContext, token).then(({ result, error }) => {
30-
if (result) return this.internalAuthenticate(request.reqContext, result, token)
35+
if (result) {
36+
return this.internalAuthenticate(request.reqContext, { payload: result, token }, request)
37+
}
3138

3239
logger.warn({ origin: this.constructor.name, error }, 'Token validation failed')
3340
switch (error) {
@@ -50,14 +57,14 @@ export abstract class JwtBasedAuthenticator<AuthInfo extends BaseAuthInfo<string
5057
* Must be implemented by subclasses to validate the payload and construct the authentication result.
5158
*
5259
* @param reqContext - The request context containing request-scoped data and logger.
53-
* @param jwtPayload - The decoded JWT payload.
54-
* @param rawToken - The original JWT token string.
60+
* @param jwt - The validated JWT containing both the decoded payload and the original token string.
61+
* @param request - The Fastify request object for accessing additional request data.
5562
* @returns The authentication result, either success with auth info or failure.
5663
*/
5764
protected abstract internalAuthenticate(
5865
reqContext: RequestContext,
59-
jwtPayload: object,
60-
rawToken: string,
66+
jwt: ValidatedJwt,
67+
request: FastifyRequest,
6168
): AuthResult<AuthInfo> | Promise<AuthResult<AuthInfo>>
6269

6370
private extractBearerToken(request: FastifyRequest): string | null {

packages/app/auth/src/fastify/createAuthenticationPreHandler.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describe('createAuthenticationPreHandler', () => {
3434
// Given
3535
const authInfo: AuthInfo = {
3636
authType: 'test-provider',
37-
rawToken: 'test-token',
37+
token: 'test-token',
3838
}
3939
const authenticator = new MockAuthenticator({ success: true, authInfo })
4040
const preHandler = createAuthenticationPreHandler(authenticator)

0 commit comments

Comments
 (0)