@elysiajs/jwt 0.6.1 → 0.6.2
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/package.json +2 -2
- package/src/index.ts +177 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elysiajs/jwt",
|
|
3
3
|
"description": "Plugin for Elysia for using JWT Authentication",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "saltyAom",
|
|
7
7
|
"url": "https://github.com/SaltyAom",
|
|
@@ -52,4 +52,4 @@
|
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"elysia": ">= 0.5.12"
|
|
54
54
|
}
|
|
55
|
-
}
|
|
55
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ValidationError,
|
|
3
|
+
getSchemaValidator,
|
|
4
|
+
Elysia,
|
|
5
|
+
type Context,
|
|
6
|
+
ElysiaInstance
|
|
7
|
+
} from 'elysia'
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
SignJWT,
|
|
11
|
+
jwtVerify,
|
|
12
|
+
type JWTPayload,
|
|
13
|
+
type JWSHeaderParameters
|
|
14
|
+
} from 'jose'
|
|
15
|
+
|
|
16
|
+
import { Type as t } from '@sinclair/typebox'
|
|
17
|
+
import type { Static, TObject, TSchema } from '@sinclair/typebox'
|
|
18
|
+
|
|
19
|
+
type UnwrapSchema<
|
|
20
|
+
Schema extends TSchema | undefined,
|
|
21
|
+
Fallback = unknown
|
|
22
|
+
> = Schema extends TSchema ? Static<NonNullable<Schema>> : Fallback
|
|
23
|
+
|
|
24
|
+
export interface JWTPayloadSpec {
|
|
25
|
+
iss?: string
|
|
26
|
+
sub?: string
|
|
27
|
+
aud?: string | string[]
|
|
28
|
+
jti?: string
|
|
29
|
+
nbf?: number
|
|
30
|
+
exp?: number
|
|
31
|
+
iat?: number
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface JWTOption<
|
|
35
|
+
Name extends string | undefined = 'jwt',
|
|
36
|
+
Schema extends TSchema | undefined = undefined
|
|
37
|
+
> extends JWSHeaderParameters,
|
|
38
|
+
Omit<JWTPayload, 'nbf' | 'exp'> {
|
|
39
|
+
/**
|
|
40
|
+
* Name to decorate method as
|
|
41
|
+
*
|
|
42
|
+
* ---
|
|
43
|
+
* @example
|
|
44
|
+
* For example, `jwt` will decorate Context with `Context.jwt`
|
|
45
|
+
*
|
|
46
|
+
* ```typescript
|
|
47
|
+
* app
|
|
48
|
+
* .decorate({
|
|
49
|
+
* name: 'myJWTNamespace',
|
|
50
|
+
* secret: process.env.JWT_SECRETS
|
|
51
|
+
* })
|
|
52
|
+
* .get('/sign/:name', ({ myJWTNamespace, params }) => {
|
|
53
|
+
* return myJWTNamespace.sign(params)
|
|
54
|
+
* })
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
name?: Name
|
|
58
|
+
/**
|
|
59
|
+
* JWT Secret
|
|
60
|
+
*/
|
|
61
|
+
secret: string
|
|
62
|
+
/**
|
|
63
|
+
* Type strict validation for JWT payload
|
|
64
|
+
*/
|
|
65
|
+
schema?: Schema
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* JWT Not Before
|
|
69
|
+
*
|
|
70
|
+
* @see [RFC7519#section-4.1.5](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.5)
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
nbf?: string | number
|
|
74
|
+
/**
|
|
75
|
+
* JWT Expiration Time
|
|
76
|
+
*
|
|
77
|
+
* @see [RFC7519#section-4.1.4](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.4)
|
|
78
|
+
*/
|
|
79
|
+
exp?: string | number
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const jwt = <
|
|
83
|
+
Name extends string = 'jwt',
|
|
84
|
+
Schema extends TSchema | undefined = undefined
|
|
85
|
+
>({
|
|
86
|
+
name = 'jwt' as Name,
|
|
87
|
+
secret,
|
|
88
|
+
// Start JWT Header
|
|
89
|
+
alg = 'HS256',
|
|
90
|
+
crit,
|
|
91
|
+
schema,
|
|
92
|
+
// End JWT Header
|
|
93
|
+
// Start JWT Payload
|
|
94
|
+
nbf,
|
|
95
|
+
exp,
|
|
96
|
+
...payload
|
|
97
|
+
}: // End JWT Payload
|
|
98
|
+
JWTOption<Name, Schema>) => {
|
|
99
|
+
if (!secret) throw new Error("Secret can't be empty")
|
|
100
|
+
|
|
101
|
+
const key = new TextEncoder().encode(secret)
|
|
102
|
+
|
|
103
|
+
const validator = schema
|
|
104
|
+
? getSchemaValidator(
|
|
105
|
+
t.Union([
|
|
106
|
+
schema as any,
|
|
107
|
+
t.Object({
|
|
108
|
+
iss: t.Optional(t.String()),
|
|
109
|
+
sub: t.Optional(t.String()),
|
|
110
|
+
aud: t.Optional(
|
|
111
|
+
t.Union([t.String(), t.Array(t.String())])
|
|
112
|
+
),
|
|
113
|
+
jti: t.Optional(t.String()),
|
|
114
|
+
nbf: t.Optional(t.Union([t.String(), t.Number()])),
|
|
115
|
+
exp: t.Optional(t.Union([t.String(), t.Number()])),
|
|
116
|
+
iat: t.Optional(t.String())
|
|
117
|
+
})
|
|
118
|
+
]) as any,
|
|
119
|
+
{}
|
|
120
|
+
)
|
|
121
|
+
: undefined
|
|
122
|
+
|
|
123
|
+
return new Elysia({
|
|
124
|
+
name: '@elysiajs/jwt',
|
|
125
|
+
seed: {
|
|
126
|
+
name,
|
|
127
|
+
secret,
|
|
128
|
+
alg,
|
|
129
|
+
crit,
|
|
130
|
+
schema,
|
|
131
|
+
nbf,
|
|
132
|
+
exp,
|
|
133
|
+
...payload
|
|
134
|
+
}
|
|
135
|
+
}).decorate(name as Name extends string ? Name : 'jwt', {
|
|
136
|
+
sign: (
|
|
137
|
+
morePayload: UnwrapSchema<Schema, Record<string, string>> &
|
|
138
|
+
JWTPayloadSpec
|
|
139
|
+
) => {
|
|
140
|
+
let jwt = new SignJWT({
|
|
141
|
+
...payload,
|
|
142
|
+
...morePayload,
|
|
143
|
+
nbf: undefined,
|
|
144
|
+
exp: undefined
|
|
145
|
+
}).setProtectedHeader({
|
|
146
|
+
alg,
|
|
147
|
+
crit
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
if (nbf) jwt = jwt.setNotBefore(nbf)
|
|
151
|
+
if (exp) jwt = jwt.setExpirationTime(exp)
|
|
152
|
+
|
|
153
|
+
return jwt.sign(key)
|
|
154
|
+
},
|
|
155
|
+
verify: async (
|
|
156
|
+
jwt?: string
|
|
157
|
+
): Promise<
|
|
158
|
+
| (UnwrapSchema<Schema, Record<string, string>> & JWTPayloadSpec)
|
|
159
|
+
| false
|
|
160
|
+
> => {
|
|
161
|
+
if (!jwt) return false
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const data: any = (await jwtVerify(jwt, key)).payload
|
|
165
|
+
|
|
166
|
+
if (validator && !validator!.Check(data))
|
|
167
|
+
throw new ValidationError('JWT', validator, data)
|
|
168
|
+
|
|
169
|
+
return data
|
|
170
|
+
} catch (_) {
|
|
171
|
+
return false
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export default jwt
|