Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
24 changed files
with
1,340 additions
and
191 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
"@directus/app": minor | ||
"@directus/api": minor | ||
--- | ||
|
||
Introduced new JSON Web Token (JWT) operation to Flows |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@directus/api': patch | ||
--- | ||
|
||
Fixed issue where type of value coming from a default value might be wrong |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@directus/specs": minor | ||
--- | ||
|
||
Added missing "update/delete multiple" endpoints to OpenAPI specs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
import jwt from 'jsonwebtoken'; | ||
import { beforeEach, expect, test, vi } from 'vitest'; | ||
|
||
import config from './index.js'; | ||
|
||
beforeEach(() => { | ||
vi.spyOn(jwt, 'sign'); | ||
vi.spyOn(jwt, 'verify'); | ||
vi.spyOn(jwt, 'decode'); | ||
}); | ||
|
||
const secret = 'some-secret'; | ||
const payload = { abc: 123 }; | ||
|
||
test('sign: should error when missing secret', async () => { | ||
const params = { | ||
operation: 'sign', | ||
}; | ||
|
||
try { | ||
await config.handler(params, {} as any); | ||
} catch (err) { | ||
expect(err).toBeDefined(); | ||
} | ||
|
||
expect(vi.mocked(jwt.sign)).not.toHaveBeenCalled(); | ||
|
||
expect.assertions(2); | ||
}); | ||
|
||
test('sign: should error when missing payload', async () => { | ||
const params = { | ||
operation: 'sign', | ||
secret, | ||
}; | ||
|
||
try { | ||
await config.handler(params, {} as any); | ||
} catch (err) { | ||
expect(err).toBeDefined(); | ||
} | ||
|
||
expect(vi.mocked(jwt.sign)).not.toHaveBeenCalled(); | ||
|
||
expect.assertions(2); | ||
}); | ||
|
||
test('sign: returns the JWT', async () => { | ||
const params = { | ||
operation: 'sign', | ||
secret, | ||
payload, | ||
}; | ||
|
||
const result = await config.handler(params, {} as any); | ||
|
||
expect(vi.mocked(jwt.sign)).toHaveBeenCalledWith(params.payload, params.secret, undefined); | ||
expect(jwt.decode(result as string)).toMatchObject(params.payload); | ||
}); | ||
|
||
test('sign: options can be set', async () => { | ||
const params = { | ||
operation: 'sign', | ||
secret, | ||
payload, | ||
options: { issuer: 'directus' }, | ||
}; | ||
|
||
const result = await config.handler(params, {} as any); | ||
|
||
expect(vi.mocked(jwt.sign)).toHaveBeenCalledWith(params.payload, params.secret, params.options); | ||
expect(jwt.decode(result as string)).toMatchObject({ ...params.payload, iss: params.options.issuer }); | ||
}); | ||
|
||
test('verify: should error when missing secret', async () => { | ||
const params = { | ||
operation: 'verify', | ||
}; | ||
|
||
try { | ||
await config.handler(params, {} as any); | ||
} catch (err) { | ||
expect(err).toBeDefined(); | ||
} | ||
|
||
expect(vi.mocked(jwt.verify)).not.toHaveBeenCalled(); | ||
|
||
expect.assertions(2); | ||
}); | ||
|
||
test('verify: should error when missing token', async () => { | ||
const params = { | ||
operation: 'verify', | ||
secret, | ||
}; | ||
|
||
try { | ||
await config.handler(params, {} as any); | ||
} catch (err) { | ||
expect(err).toBeDefined(); | ||
} | ||
|
||
expect(vi.mocked(jwt.verify)).not.toHaveBeenCalled(); | ||
|
||
expect.assertions(2); | ||
}); | ||
|
||
test('verify: should error with incorrect secret', async () => { | ||
const params = { | ||
operation: 'verify', | ||
secret: 'invalid-secret', | ||
token: jwt.sign(payload, secret), | ||
}; | ||
|
||
try { | ||
await config.handler(params, {} as any); | ||
} catch (err) { | ||
expect(err).toBeDefined(); | ||
} | ||
|
||
expect(vi.mocked(jwt.verify)).toHaveBeenCalledWith(params.token, params.secret, undefined); | ||
|
||
expect.assertions(2); | ||
}); | ||
|
||
test('verify: returns the payload', async () => { | ||
const params = { | ||
operation: 'verify', | ||
secret, | ||
token: jwt.sign(payload, secret), | ||
}; | ||
|
||
const result = await config.handler(params, {} as any); | ||
|
||
expect(vi.mocked(jwt.verify)).toHaveBeenCalledWith(params.token, params.secret, undefined); | ||
expect(result).toMatchObject(payload); | ||
}); | ||
|
||
test('verify: options can be set', async () => { | ||
const params = { | ||
operation: 'verify', | ||
secret, | ||
token: jwt.sign(payload, secret), | ||
options: { | ||
complete: true, | ||
}, | ||
}; | ||
|
||
const result = await config.handler(params, {} as any); | ||
|
||
expect(vi.mocked(jwt.verify)).toHaveBeenCalledWith(params.token, params.secret, params.options); | ||
|
||
expect(result).toMatchObject({ | ||
header: { alg: 'HS256' }, | ||
payload, | ||
signature: params.token.split('.')[2], | ||
}); | ||
}); | ||
|
||
test('decode: should error when missing token', async () => { | ||
const params = { | ||
operation: 'decode', | ||
}; | ||
|
||
try { | ||
await config.handler(params, {} as any); | ||
} catch (err) { | ||
expect(err).toBeDefined(); | ||
} | ||
|
||
expect(vi.mocked(jwt.decode)).not.toHaveBeenCalled(); | ||
|
||
expect.assertions(2); | ||
}); | ||
|
||
test('decode: returns the payload', async () => { | ||
const params = { | ||
operation: 'decode', | ||
token: jwt.sign(payload, secret), | ||
}; | ||
|
||
const result = await config.handler(params, {} as any); | ||
|
||
expect(vi.mocked(jwt.decode)).toHaveBeenCalledWith(params.token, undefined); | ||
expect(result).toMatchObject(payload); | ||
}); | ||
|
||
test('decode: options can be set', async () => { | ||
const params = { | ||
operation: 'decode', | ||
token: jwt.sign(payload, secret), | ||
options: { | ||
complete: true, | ||
}, | ||
}; | ||
|
||
const result = await config.handler(params, {} as any); | ||
|
||
expect(vi.mocked(jwt.decode)).toHaveBeenCalledWith(params.token, params.options); | ||
|
||
expect(result).toMatchObject({ | ||
header: { alg: 'HS256' }, | ||
payload, | ||
signature: params.token.split('.')[2], | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { defineOperationApi, optionToObject, optionToString } from '@directus/utils'; | ||
import jwt from 'jsonwebtoken'; | ||
|
||
type Options = { | ||
operation: string; | ||
payload?: Record<string, any> | string; | ||
token?: string; | ||
secret?: jwt.Secret; | ||
options?: any; | ||
}; | ||
|
||
export default defineOperationApi<Options>({ | ||
id: 'json-web-token', | ||
|
||
handler: async ({ operation, payload, token, secret, options }) => { | ||
if (operation === 'sign') { | ||
if (!payload) throw new Error('Undefined JSON Web Token payload'); | ||
if (!secret) throw new Error('Undefined JSON Web Token secret'); | ||
|
||
const payloadObject: any = optionToObject(payload); | ||
const secretString = optionToString(secret); | ||
const optionsObject = optionToObject(options); | ||
|
||
return jwt.sign(payloadObject, secretString, optionsObject); | ||
} else if (operation === 'verify') { | ||
if (!token) throw new Error('Undefined JSON Web Token token'); | ||
if (!secret) throw new Error('Undefined JSON Web Token secret'); | ||
|
||
const tokenString = optionToString(token); | ||
const secretString = optionToString(secret); | ||
const optionsObject = optionToObject(options); | ||
|
||
return jwt.verify(tokenString, secretString, optionsObject); | ||
} else if (operation === 'decode') { | ||
if (!token) throw new Error('Undefined JSON Web Token token'); | ||
|
||
const tokenString = optionToString(token); | ||
const optionsObject = optionToObject(options); | ||
|
||
return jwt.decode(tokenString, optionsObject); | ||
} | ||
|
||
throw new Error('Undefined "Operation" for JSON Web Token'); | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.