@elysiajs/jwt 0.6.2 → 0.6.4

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/README.md CHANGED
@@ -1,104 +1,121 @@
1
1
  # @elysiajs/static
2
+
2
3
  Plugin for [Elysia](https://github.com/elysiajs/elysia) for using JWT Authentication.
3
4
 
4
5
  ## Installation
6
+
5
7
  ```bash
6
8
  bun add @elysiajs/jwt
7
9
  ```
8
10
 
9
11
  ## Example
12
+
10
13
  ```typescript
11
- import { Elysia, t } from 'elysia'
12
- import { jwt } from '@elysiajs/jwt'
13
- import { cookie } from '@elysiajs/cookie'
14
+ import { Elysia, t } from 'elysia';
15
+ import { jwt } from '@elysiajs/jwt';
16
+ import { cookie } from '@elysiajs/cookie';
14
17
 
15
18
  const app = new Elysia()
16
- .use(
17
- jwt({
18
- name: 'jwt',
19
- // This should be Environment Variable
20
- secret: 'MY_SECRETS'
21
- })
22
- )
23
- .use(cookie())
24
- .get('/sign/:name', async ({ jwt, cookie, setCookie, params }) => {
25
- setCookie('auth', await jwt.sign(params), {
26
- httpOnly: true
27
- })
28
-
29
- return `Sign in as ${params.name}`
30
- })
31
- .get('/profile', async ({ jwt, set, cookie: { auth } }) => {
32
- const profile = await jwt.verify(auth)
33
-
34
- if (!profile) {
35
- set.status = 401
36
- return 'Unauthorized'
37
- }
38
-
39
- return `Hello ${profile.name}`
19
+ .use(
20
+ jwt({
21
+ name: 'jwt',
22
+ // This should be Environment Variable
23
+ secret: 'MY_SECRETS',
40
24
  })
41
- .listen(8080)
25
+ )
26
+ .use(cookie())
27
+ .get('/sign/:name', async ({ jwt, cookie, setCookie, params }) => {
28
+ setCookie('auth', await jwt.sign(params), {
29
+ httpOnly: true,
30
+ });
31
+
32
+ return `Sign in as ${params.name}`;
33
+ })
34
+ .get('/profile', async ({ jwt, set, cookie: { auth } }) => {
35
+ const profile = await jwt.verify(auth);
36
+
37
+ if (!profile) {
38
+ set.status = 401;
39
+ return 'Unauthorized';
40
+ }
41
+
42
+ return `Hello ${profile.name}`;
43
+ })
44
+ .listen(8080);
42
45
  ```
43
46
 
44
47
  ## Config
45
- This package extends [jose](https://github.com/panva/jose), most config is inherited from Jose.
48
+
49
+ This package extends [jose](https://github.com/panva/jose), most config is inherited from Jose.
46
50
 
47
51
  Below are configurable properties for using JWT plugin
48
52
 
49
53
  ### name
54
+
50
55
  Name to decorate method as:
51
56
 
52
57
  For example, `jwt` will decorate Context with `Context.jwt`
53
58
 
54
59
  ### secret
60
+
55
61
  JWT secret key
56
62
 
57
63
  ### schema
64
+
58
65
  Type strict validation for JWT payload
59
66
 
60
67
  ## Jose's config
68
+
61
69
  Below is the config inherits from [jose](https://github.com/panva/jose)
62
70
 
63
71
  ### alg
72
+
64
73
  @default 'HS256'
65
74
 
66
75
  Algorithm to sign JWT with
67
76
 
68
77
  ### crit
78
+
69
79
  Critical Header Parameter.
70
80
 
71
81
  ### iss
82
+
72
83
  JWT Issuer
73
84
 
74
85
  @see [RFC7519#section-4.1.1](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.1)
75
86
 
76
87
  ### sub
88
+
77
89
  JWT Subject
78
90
 
79
91
  @see [RFC7519#section-4.1.2](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.2)
80
92
 
81
93
  ### aud
82
- JWT Audience
94
+
95
+ JWT Audience
83
96
 
84
97
  @see [RFC7519#section-4.1.3](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3)
85
98
 
86
99
  ### jti
100
+
87
101
  JWT ID
88
102
 
89
103
  @see [RFC7519#section-4.1.7](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.7)
90
104
 
91
105
  ### nbf
106
+
92
107
  JWT Not Before
93
108
 
94
109
  @see [RFC7519#section-4.1.5](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.5)
95
110
 
96
111
  ### exp
112
+
97
113
  JWT Expiration Time
98
114
 
99
115
  @see [RFC7519#section-4.1.4](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.4)
100
116
 
101
117
  ### iat
118
+
102
119
  JWT Issued At
103
120
 
104
121
  @see [RFC7519#section-4.1.6](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.6)
@@ -1,5 +1,5 @@
1
1
  import { Elysia } from 'elysia';
2
- import { type JWTPayload, type JWSHeaderParameters } from 'jose';
2
+ import { type JWTPayload, type JWSHeaderParameters, type KeyLike } from 'jose';
3
3
  import type { Static, TSchema } from '@sinclair/typebox';
4
4
  type UnwrapSchema<Schema extends TSchema | undefined, Fallback = unknown> = Schema extends TSchema ? Static<NonNullable<Schema>> : Fallback;
5
5
  export interface JWTPayloadSpec {
@@ -13,7 +13,7 @@ export interface JWTPayloadSpec {
13
13
  }
14
14
  export interface JWTOption<Name extends string | undefined = 'jwt', Schema extends TSchema | undefined = undefined> extends JWSHeaderParameters, Omit<JWTPayload, 'nbf' | 'exp'> {
15
15
  name?: Name;
16
- secret: string;
16
+ secret: string | Uint8Array | KeyLike;
17
17
  schema?: Schema;
18
18
  nbf?: string | number;
19
19
  exp?: string | number;
package/dist/cjs/index.js CHANGED
@@ -7,9 +7,9 @@ const typebox_1 = require("@sinclair/typebox");
7
7
  const jwt = ({ name = 'jwt', secret, alg = 'HS256', crit, schema, nbf, exp, ...payload }) => {
8
8
  if (!secret)
9
9
  throw new Error("Secret can't be empty");
10
- const key = new TextEncoder().encode(secret);
10
+ const key = typeof secret === 'string' ? new TextEncoder().encode(secret) : secret;
11
11
  const validator = schema
12
- ? (0, elysia_1.getSchemaValidator)(typebox_1.Type.Union([
12
+ ? (0, elysia_1.getSchemaValidator)(typebox_1.Type.Intersect([
13
13
  schema,
14
14
  typebox_1.Type.Object({
15
15
  iss: typebox_1.Type.Optional(typebox_1.Type.String()),
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Elysia } from 'elysia';
2
- import { type JWTPayload, type JWSHeaderParameters } from 'jose';
2
+ import { type JWTPayload, type JWSHeaderParameters, type KeyLike } from 'jose';
3
3
  import type { Static, TSchema } from '@sinclair/typebox';
4
4
  type UnwrapSchema<Schema extends TSchema | undefined, Fallback = unknown> = Schema extends TSchema ? Static<NonNullable<Schema>> : Fallback;
5
5
  export interface JWTPayloadSpec {
@@ -13,7 +13,7 @@ export interface JWTPayloadSpec {
13
13
  }
14
14
  export interface JWTOption<Name extends string | undefined = 'jwt', Schema extends TSchema | undefined = undefined> extends JWSHeaderParameters, Omit<JWTPayload, 'nbf' | 'exp'> {
15
15
  name?: Name;
16
- secret: string;
16
+ secret: string | Uint8Array | KeyLike;
17
17
  schema?: Schema;
18
18
  nbf?: string | number;
19
19
  exp?: string | number;
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
- import { ValidationError, getSchemaValidator, Elysia } from 'elysia';
1
+ import { Elysia, ValidationError, getSchemaValidator } from 'elysia';
2
2
  import { SignJWT, jwtVerify } from 'jose';
3
3
  import { Type as t } from '@sinclair/typebox';
4
4
  export const jwt = ({ name = 'jwt', secret, alg = 'HS256', crit, schema, nbf, exp, ...payload }) => {
5
5
  if (!secret)
6
6
  throw new Error("Secret can't be empty");
7
- const key = new TextEncoder().encode(secret);
7
+ const key = typeof secret === 'string' ? new TextEncoder().encode(secret) : secret;
8
8
  const validator = schema
9
- ? getSchemaValidator(t.Union([
9
+ ? getSchemaValidator(t.Intersect([
10
10
  schema,
11
11
  t.Object({
12
12
  iss: t.Optional(t.String()),
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.2",
4
+ "version": "0.6.4",
5
5
  "author": {
6
6
  "name": "saltyAom",
7
7
  "url": "https://github.com/SaltyAom",
@@ -19,7 +19,7 @@
19
19
  "import": "./dist/index.js",
20
20
  "default": "./dist/index.js"
21
21
  },
22
- "types": "./dist/index.ts",
22
+ "types": "./dist/index.d.ts",
23
23
  "bugs": "https://github.com/elysiajs/elysia-jwt/issues",
24
24
  "homepage": "https://github.com/elysiajs/elysia-jwt",
25
25
  "keywords": [
@@ -31,7 +31,7 @@
31
31
  "license": "MIT",
32
32
  "scripts": {
33
33
  "dev": "bun run --hot example/index.ts",
34
- "test": "bun wiptest && npm run test:node",
34
+ "test": "bun test && npm run test:node",
35
35
  "test:node": "npm install --prefix ./test/node/cjs/ && npm install --prefix ./test/node/esm/ && node ./test/node/cjs/index.js && node ./test/node/esm/index.js",
36
36
  "build": "rimraf dist && tsc --project tsconfig.esm.json && tsc --project tsconfig.cjs.json",
37
37
  "release": "npm run build && npm run test && npm publish --access public"
@@ -43,6 +43,8 @@
43
43
  "@elysiajs/cookie": "^0.3.0",
44
44
  "@sinclair/typebox": "^0.30.4",
45
45
  "@types/node": "^20.1.4",
46
+ "@typescript-eslint/eslint-plugin": "^6.6.0",
47
+ "@typescript-eslint/parser": "^6.6.0",
46
48
  "bun-types": "^0.5.8",
47
49
  "elysia": "0.6.6",
48
50
  "eslint": "^8.40.0",
@@ -50,6 +52,6 @@
50
52
  "typescript": "^5.1.6"
51
53
  },
52
54
  "peerDependencies": {
53
- "elysia": ">= 0.5.12"
55
+ "elysia": ">= 0.6.0"
54
56
  }
55
57
  }
package/src/index.ts DELETED
@@ -1,177 +0,0 @@
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