mirror of
https://github.com/lowdefy/lowdefy.git
synced 2025-02-17 14:30:34 +08:00
fix(api): JSON web token tests and fixes.
This commit is contained in:
parent
cd82729c5d
commit
30f7267c3e
89
packages/api/src/routes/auth/issueAccessToken.test.js
Normal file
89
packages/api/src/routes/auth/issueAccessToken.test.js
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020-2021 Lowdefy, Inc
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
|
import testContext from '../../test/testContext';
|
||||||
|
import issueAccessToken from './issueAccessToken';
|
||||||
|
|
||||||
|
const secrets = {
|
||||||
|
JWT_SECRET: 'JWT_SECRET',
|
||||||
|
};
|
||||||
|
|
||||||
|
const RealDate = Date.now;
|
||||||
|
|
||||||
|
const mockNow = jest.fn();
|
||||||
|
mockNow.mockImplementation(() => 1000);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
global.Date.now = mockNow;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
global.Date.now = RealDate;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('issueAccessToken', () => {
|
||||||
|
const accessToken = issueAccessToken(testContext({ host: 'host', secrets }), {
|
||||||
|
claims: { sub: 'sub', email: 'email' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const claims = jwt.verify(accessToken, 'JWT_SECRET', {
|
||||||
|
algorithms: ['HS256'],
|
||||||
|
audience: 'host',
|
||||||
|
issuer: 'host',
|
||||||
|
});
|
||||||
|
expect(claims).toEqual({
|
||||||
|
aud: 'host',
|
||||||
|
email: 'email',
|
||||||
|
exp: 14401, // 4 hours
|
||||||
|
iat: 1,
|
||||||
|
iss: 'host',
|
||||||
|
sub: 'sub',
|
||||||
|
lowdefy_access_token: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('issueAccessToken, configure token expiry', () => {
|
||||||
|
const accessToken = issueAccessToken(
|
||||||
|
testContext({
|
||||||
|
config: {
|
||||||
|
auth: {
|
||||||
|
jwt: {
|
||||||
|
expiresIn: '12h',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
host: 'host',
|
||||||
|
secrets,
|
||||||
|
}),
|
||||||
|
{ claims: { sub: 'sub', email: 'email' } }
|
||||||
|
);
|
||||||
|
const claims = jwt.verify(accessToken, 'JWT_SECRET', {
|
||||||
|
algorithms: ['HS256'],
|
||||||
|
audience: 'host',
|
||||||
|
issuer: 'host',
|
||||||
|
});
|
||||||
|
expect(claims).toEqual({
|
||||||
|
aud: 'host',
|
||||||
|
email: 'email',
|
||||||
|
exp: 43201, // 12 hours
|
||||||
|
iat: 1,
|
||||||
|
iss: 'host',
|
||||||
|
sub: 'sub',
|
||||||
|
lowdefy_access_token: true,
|
||||||
|
});
|
||||||
|
});
|
92
packages/api/src/routes/auth/issueOpenIdStateToken.test.js
Normal file
92
packages/api/src/routes/auth/issueOpenIdStateToken.test.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020-2021 Lowdefy, Inc
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2020-2021 Lowdefy, Inc
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
|
import testContext from '../../test/testContext';
|
||||||
|
import issueOpenIdStateToken from './issueOpenIdStateToken';
|
||||||
|
|
||||||
|
const secrets = {
|
||||||
|
JWT_SECRET: 'JWT_SECRET',
|
||||||
|
};
|
||||||
|
|
||||||
|
const RealDate = Date.now;
|
||||||
|
|
||||||
|
const mockNow = jest.fn();
|
||||||
|
mockNow.mockImplementation(() => 1000);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
global.Date.now = mockNow;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
global.Date.now = RealDate;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('issueOpenIdStateToken', async () => {
|
||||||
|
const stateToken = issueOpenIdStateToken(testContext({ host: 'host', secrets }), {
|
||||||
|
input: { i: true },
|
||||||
|
pageId: 'pageId',
|
||||||
|
urlQuery: { u: true },
|
||||||
|
});
|
||||||
|
const claims = jwt.verify(stateToken, 'JWT_SECRET', {
|
||||||
|
algorithms: ['HS256'],
|
||||||
|
audience: 'host',
|
||||||
|
issuer: 'host',
|
||||||
|
});
|
||||||
|
expect(claims).toEqual({
|
||||||
|
aud: 'host',
|
||||||
|
exp: 301, // 5min
|
||||||
|
iat: 1,
|
||||||
|
input: { i: true },
|
||||||
|
iss: 'host',
|
||||||
|
lowdefy_openid_state_token: true,
|
||||||
|
pageId: 'pageId',
|
||||||
|
urlQuery: { u: true },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('issueOpenIdStateToken, no location data', async () => {
|
||||||
|
const stateToken = issueOpenIdStateToken(testContext({ host: 'host', secrets }), {});
|
||||||
|
const claims = jwt.verify(stateToken, 'JWT_SECRET', {
|
||||||
|
algorithms: ['HS256'],
|
||||||
|
audience: 'host',
|
||||||
|
issuer: 'host',
|
||||||
|
});
|
||||||
|
expect(claims).toEqual({
|
||||||
|
aud: 'host',
|
||||||
|
exp: 301, // 5min
|
||||||
|
iat: 1,
|
||||||
|
iss: 'host',
|
||||||
|
lowdefy_openid_state_token: true,
|
||||||
|
});
|
||||||
|
});
|
97
packages/api/src/routes/auth/verifyAccessToken.test.js
Normal file
97
packages/api/src/routes/auth/verifyAccessToken.test.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020-2021 Lowdefy, Inc
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
|
import testContext from '../../test/testContext';
|
||||||
|
import issueAccessToken from './issueAccessToken';
|
||||||
|
import issueOpenIdStateToken from './issueOpenIdStateToken';
|
||||||
|
import verifyAccessToken from './verifyAccessToken';
|
||||||
|
|
||||||
|
import { AuthenticationError, TokenExpiredError } from '../../context/errors';
|
||||||
|
|
||||||
|
const secrets = {
|
||||||
|
JWT_SECRET: 'JWT_SECRET',
|
||||||
|
};
|
||||||
|
|
||||||
|
const context = testContext({ host: 'host', secrets });
|
||||||
|
|
||||||
|
const RealDate = Date.now;
|
||||||
|
|
||||||
|
const mockNow = jest.fn();
|
||||||
|
mockNow.mockImplementation(() => 1000);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
global.Date.now = mockNow;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
global.Date.now = RealDate;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('verifyAccessToken', () => {
|
||||||
|
const token = issueAccessToken(context, {
|
||||||
|
claims: { sub: 'sub', email: 'email' },
|
||||||
|
});
|
||||||
|
const claims = verifyAccessToken(context, { token });
|
||||||
|
expect(claims).toEqual({
|
||||||
|
aud: 'host',
|
||||||
|
email: 'email',
|
||||||
|
exp: 14401, // 4 hours
|
||||||
|
iat: 1,
|
||||||
|
iss: 'host',
|
||||||
|
sub: 'sub',
|
||||||
|
lowdefy_access_token: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('verifyAccessToken openIdState token invalid', () => {
|
||||||
|
const token = issueOpenIdStateToken(context, {
|
||||||
|
input: { i: true },
|
||||||
|
pageId: 'pageId',
|
||||||
|
urlQuery: { u: true },
|
||||||
|
});
|
||||||
|
expect(() => verifyAccessToken(context, { token })).toThrow(AuthenticationError);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('verifyAccessToken invalid token', () => {
|
||||||
|
expect(() => verifyAccessToken(context, { token: 'not a token' })).toThrow(AuthenticationError);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('verifyAccessToken no sub invalid token', () => {
|
||||||
|
const token = issueAccessToken(context, {
|
||||||
|
claims: {
|
||||||
|
email: 'email',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(() => verifyAccessToken(context, { token })).toThrow(AuthenticationError);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('verifyAccessToken token expired', () => {
|
||||||
|
const token = jwt.sign(
|
||||||
|
{
|
||||||
|
sub: 'sub',
|
||||||
|
lowdefy_access_token: true,
|
||||||
|
exp: -10000,
|
||||||
|
},
|
||||||
|
'JWT_SECRET',
|
||||||
|
{
|
||||||
|
audience: 'host',
|
||||||
|
issuer: 'host',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
expect(() => verifyAccessToken(context, { token })).toThrow(TokenExpiredError);
|
||||||
|
});
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import { AuthenticationError, TokenExpiredError } from '../../context/errors';
|
import { AuthenticationError } from '../../context/errors';
|
||||||
|
|
||||||
function verifyOpenIdStateToken({ host, secrets }, { token }) {
|
function verifyOpenIdStateToken({ host, secrets }, { token }) {
|
||||||
try {
|
try {
|
||||||
@ -31,7 +31,7 @@ function verifyOpenIdStateToken({ host, secrets }, { token }) {
|
|||||||
return claims;
|
return claims;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.name === 'TokenExpiredError') {
|
if (err.name === 'TokenExpiredError') {
|
||||||
throw new TokenExpiredError('Token expired.');
|
throw new AuthenticationError('Token expired.');
|
||||||
} else {
|
} else {
|
||||||
throw new AuthenticationError('Invalid token.');
|
throw new AuthenticationError('Invalid token.');
|
||||||
}
|
}
|
||||||
|
90
packages/api/src/routes/auth/verifyOpenIdStateToken.test.js
Normal file
90
packages/api/src/routes/auth/verifyOpenIdStateToken.test.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020-2021 Lowdefy, Inc
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
|
import testContext from '../../test/testContext';
|
||||||
|
import issueAccessToken from './issueAccessToken';
|
||||||
|
import issueOpenIdStateToken from './issueOpenIdStateToken';
|
||||||
|
import verifyOpenIdStateToken from './verifyOpenIdStateToken';
|
||||||
|
|
||||||
|
import { AuthenticationError } from '../../context/errors';
|
||||||
|
|
||||||
|
const secrets = {
|
||||||
|
JWT_SECRET: 'JWT_SECRET',
|
||||||
|
};
|
||||||
|
|
||||||
|
const context = testContext({ host: 'host', secrets });
|
||||||
|
|
||||||
|
const RealDate = Date.now;
|
||||||
|
|
||||||
|
const mockNow = jest.fn();
|
||||||
|
mockNow.mockImplementation(() => 1000);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
global.Date.now = mockNow;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
global.Date.now = RealDate;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('verifyOpenIdStateToken', () => {
|
||||||
|
const token = issueOpenIdStateToken(context, {
|
||||||
|
input: { i: true },
|
||||||
|
pageId: 'pageId',
|
||||||
|
urlQuery: { u: true },
|
||||||
|
});
|
||||||
|
const claims = verifyOpenIdStateToken(context, { token });
|
||||||
|
expect(claims).toEqual({
|
||||||
|
aud: 'host',
|
||||||
|
exp: 301, // 5min
|
||||||
|
iat: 1,
|
||||||
|
input: { i: true },
|
||||||
|
iss: 'host',
|
||||||
|
lowdefy_openid_state_token: true,
|
||||||
|
pageId: 'pageId',
|
||||||
|
urlQuery: { u: true },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('verifyOpenIdStateToken access token invalid', () => {
|
||||||
|
const token = issueAccessToken(context, {
|
||||||
|
claims: { sub: 'sub', email: 'email' },
|
||||||
|
});
|
||||||
|
expect(() => verifyOpenIdStateToken(context, { token })).toThrow(AuthenticationError);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('verifyOpenIdStateToken invalid token', () => {
|
||||||
|
expect(() => verifyOpenIdStateToken(context, { token: 'not a token' })).toThrow(
|
||||||
|
AuthenticationError
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('verifyOpenIdStateToken token expired', () => {
|
||||||
|
const token = jwt.sign(
|
||||||
|
{
|
||||||
|
lowdefy_openid_state_token: true,
|
||||||
|
exp: -10000,
|
||||||
|
},
|
||||||
|
'JWT_SECRET',
|
||||||
|
{
|
||||||
|
audience: 'host',
|
||||||
|
issuer: 'host',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
expect(() => verifyOpenIdStateToken(context, { token })).toThrow(AuthenticationError);
|
||||||
|
});
|
@ -28,8 +28,8 @@ async function run() {
|
|||||||
// configDirectory: path.resolve(process.cwd(), '../docs'),
|
// configDirectory: path.resolve(process.cwd(), '../docs'),
|
||||||
// configDirectory: path.resolve(process.cwd(), '../servers/serverDev'),
|
// configDirectory: path.resolve(process.cwd(), '../servers/serverDev'),
|
||||||
configDirectory: process.cwd(),
|
configDirectory: process.cwd(),
|
||||||
// outputDirectory: path.resolve(process.cwd(), '../servers/serverDev/.lowdefy/build'),
|
outputDirectory: path.resolve(process.cwd(), '../servers/serverDev/.lowdefy/build'),
|
||||||
outputDirectory: path.resolve(process.cwd(), './.lowdefy/build'),
|
// outputDirectory: path.resolve(process.cwd(), './.lowdefy/build'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user