@velocitycareerlabs/server-careerwallet 1.25.0-dev-build.12642c864
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/.localdev.env +47 -0
- package/.standalone.env +13 -0
- package/LICENSE +248 -0
- package/jest.config.js +20 -0
- package/migrate-mongo.config.js +25 -0
- package/migrations/20211017180227-create-personas.js +478 -0
- package/migrations/20211026185916-create-vanessa.js +79 -0
- package/migrations/20211026185917-update-personas.js +30 -0
- package/migrations/20211108124410-remove-surplus-personas.js +33 -0
- package/migrations/20211108132353-fix-vanessa-and-sheila.js +25 -0
- package/migrations/20220222123110-add-career-wallet-app-config.js +6 -0
- package/migrations/20220411104157-add-min-app-versions.js +15 -0
- package/migrations/20220419131726-create-nicole-flores-persona.js +63 -0
- package/migrations/20220515114034-update-persona-id-credentials.js +628 -0
- package/migrations/20220608093743-disable-mainnet-holderapp-id-verification.js +21 -0
- package/migrations/20220609063708-enable-mainnet-holderapp-id-verification.js +21 -0
- package/migrations/20220623091507-add-push-url.js +43 -0
- package/migrations/20220624133205-set-min-app-versions-to-11.js +16 -0
- package/migrations/20220710125326-set-min-app-version-to-0.10.7.js +16 -0
- package/migrations/20220811103500-add-verification-service-disclosure-deeplink.js +45 -0
- package/migrations/20220811123751-add-holderapp-dids-to-config.js +74 -0
- package/migrations/20220818072306-add-holderapp-endpoints-to-config.js +21 -0
- package/migrations/20220825090656-update-deeplink.js +46 -0
- package/migrations/20221003151823-app-config-add-public-verification-api.js +18 -0
- package/migrations/20221116085242-add-holderapp-sdk-to-config.js +15 -0
- package/migrations/20221121091030-update-holderapp-deeplink.js +19 -0
- package/migrations/20221128103425-update-holderapp-presentation-template.js +19 -0
- package/migrations/20221221091436-app-config-add-oauth.js +13 -0
- package/migrations/20221226205900-app-config-add-presentation-extension-api.js +18 -0
- package/migrations/20230120113141-update-holderapp-cashSiquence.js +15 -0
- package/migrations/20230123084103-update-holderapp-cacheSequence.js +15 -0
- package/migrations/20230214083430-update-holderapp-cache-Sequence.js +15 -0
- package/migrations/20230225173335-set-min-app-version-1.5.1.js +30 -0
- package/migrations/20230323120629-add-sunil-singh-persona.js +87 -0
- package/migrations/20230329081529-add-personas-by-env.js +294 -0
- package/migrations/20230329103219-remove-unused-personas.js +14 -0
- package/migrations/20230504090208-disable-yoti-migration.js +21 -0
- package/migrations/20230504123425-set-min-app-version-1.8.1.js +38 -0
- package/migrations/20230504185047-enable-yoti-migration.js +21 -0
- package/migrations/20230524053203-add-devices-index.js +16 -0
- package/migrations/20230704000002-add-common-holder-endpoints-to-config.js +16 -0
- package/migrations/20230704000003-add-linkedin-holder-endpoints-to-config.js +18 -0
- package/migrations/20230704104055-update-push-url-and-yoti-url.js +18 -0
- package/migrations/20230705000001-add-liburl-to-holderapp-config.js +20 -0
- package/migrations/20230814113134-app-config-add-oauth-client-id.js +18 -0
- package/migrations/20230821154136-yoti-new-session-url-fix.js +19 -0
- package/migrations/20230907134442-update-holderapp-cache-Sequence.js +15 -0
- package/migrations/20230919180000-set-holderapp-min-app-versions-1.15.0.js +16 -0
- package/migrations/20231011083137-update-holderapp-cache-sequence-6.js +15 -0
- package/migrations/20231102083252-set-holderapp-min-app-versions-1.14.0.js +16 -0
- package/migrations/20231108202229-set-holderapp-min-app-versions-1.15.0.js +16 -0
- package/migrations/20231115143332-holderapp-isDirectIssuerCheckOn-true.js +16 -0
- package/migrations/20231120100020-insert-didKeyMetadatum-for-old-accounts.js +59 -0
- package/migrations/20231207142742-remove-devices-from-accounts.js +46 -0
- package/migrations/20231226090805-app-config-set-direct-issuer-check.js +13 -0
- package/migrations/202312271524-set-holderapp-app-version-1.15.1.js +16 -0
- package/migrations/20232211171700-app-config-update-base-urls.js +18 -0
- package/migrations/20240102093506-holderapp-revert-version-1.15.0.js +16 -0
- package/migrations/202401041618111-holderapp-isDebugOn-false.js +17 -0
- package/migrations/20240131095122-test-personas-add-did.js +102 -0
- package/migrations/202402051547000-update-sdk.js +18 -0
- package/migrations/20240206101448-update-personas-vc-to-v2.js +137 -0
- package/migrations/202402061233000-set-xVnfProtocolVersion-2.js +13 -0
- package/migrations/202402081240000-set-xVnfProtocolVersion-1.js +13 -0
- package/migrations/202402131319-set-holderapp-min-versions-1.17.js +16 -0
- package/migrations/202402141152-set-holderapp-min-version-1.16.js +16 -0
- package/migrations/20240221123501-transform-account-keys-to-stringified-jwk.js +81 -0
- package/migrations/202402290955-update-holderapp-to-verifyMyCreds.js +36 -0
- package/migrations/20240311134223-update-keyid-persona.js +66 -0
- package/migrations/20240312141618-vl-7409-persona-update-maria-williams.js +82 -0
- package/migrations/202403181733000-remove-devices-from-all-accounts.js +27 -0
- package/migrations/20240401091041-set-holderapp-min-version-1.18.1.js +16 -0
- package/migrations/202404071847-app-config-update-yoti-url.js +19 -0
- package/migrations/20240724063405-vl-3827-new-yoti-session-url.js +19 -0
- package/migrations/20240731112302-vl-8160-add-persona-keys.js +101 -0
- package/migrations/20240911115206-vanessa-lin-id-credentials.js +57 -0
- package/migrations/202409201219-add-isWalletAvailable-field.js +13 -0
- package/migrations/202409221012-set-isWalletAvailable-true.js +13 -0
- package/migrations/20240922114643-holderapp-isWalletAvailable-refactor.js +36 -0
- package/migrations/20240923132213-update-android-ios-app-config.js +36 -0
- package/migrations/20240926061732-inc-cacheSequence-to-7.js +15 -0
- package/migrations/20240926073339-inc-cacheSequence-to-7-fix.js +18 -0
- package/migrations/202410081217-adam-smith-id-credntials.js +57 -0
- package/migrations/202410101243-adam_smith-id-credential.js +57 -0
- package/migrations/20241015124307-persona-key-id-type-fix.js +26 -0
- package/migrations/202501291027000-set-holderapp-did-web.js +37 -0
- package/migrations/environments/dev.env +34 -0
- package/migrations/environments/localdev.env +22 -0
- package/migrations/environments/prod.env +18 -0
- package/migrations/environments/qa.env +54 -0
- package/migrations/environments/staging.env +31 -0
- package/migrations/environments/test.env +18 -0
- package/package.json +84 -0
- package/src/assets/category-icons/assessment.png +0 -0
- package/src/assets/category-icons/badge.png +0 -0
- package/src/assets/category-icons/certification.png +0 -0
- package/src/assets/category-icons/education.png +0 -0
- package/src/assets/category-icons/employment.png +0 -0
- package/src/assets/category-icons/gig.png +0 -0
- package/src/assets/category-icons/identity.png +0 -0
- package/src/assets/category-icons/pharmacy.png +0 -0
- package/src/assets/category-icons/training.png +0 -0
- package/src/assets/credentialCategories.json +119 -0
- package/src/config/config.js +156 -0
- package/src/controllers/api/v0.6/accounts/autohooks.js +36 -0
- package/src/controllers/api/v0.6/accounts/controller.js +288 -0
- package/src/controllers/api/v0.6/accounts/schemas/careerwallet-accounts-didkeymetadatum-response.schema.js +41 -0
- package/src/controllers/api/v0.6/accounts/schemas/careerwallet-accounts-request.schema.js +49 -0
- package/src/controllers/api/v0.6/accounts/schemas/careerwallet-accounts-response.schema.js +74 -0
- package/src/controllers/api/v0.6/accounts/schemas/careerwallet-get-account-response.schema.js +72 -0
- package/src/controllers/api/v0.6/accounts/schemas/index.js +6 -0
- package/src/controllers/api/v0.6/careerwallet/appconfig/controller.js +25 -0
- package/src/controllers/api/v0.6/careerwallet/appconfig/repo.js +40 -0
- package/src/controllers/api/v0.6/careerwallet/autohooks.js +5 -0
- package/src/controllers/api/v0.6/careerwallet/consents/controller.js +66 -0
- package/src/controllers/api/v0.6/careerwallet/consents/latest/autohooks.js +7 -0
- package/src/controllers/api/v0.6/careerwallet/consents/latest/controller.js +76 -0
- package/src/controllers/api/v0.6/careerwallet/consents/repo.js +24 -0
- package/src/controllers/api/v0.6/careerwallet/consents/schemas/careerwallet-consent-response.schema.js +23 -0
- package/src/controllers/api/v0.6/careerwallet/consents/schemas/index.js +3 -0
- package/src/controllers/api/v0.6/create_did_key/autohooks.js +10 -0
- package/src/controllers/api/v0.6/create_did_key/controller.js +84 -0
- package/src/controllers/api/v0.6/create_did_key/schemas/index.js +4 -0
- package/src/controllers/api/v0.6/create_did_key/schemas/jwk-did-request.schema.js +20 -0
- package/src/controllers/api/v0.6/create_did_key/schemas/jwk-did-response.schema.js +41 -0
- package/src/controllers/api/v0.6/create_jwk/autohooks.js +10 -0
- package/src/controllers/api/v0.6/create_jwk/controller.js +46 -0
- package/src/controllers/api/v0.6/create_jwk/schemas/index.js +3 -0
- package/src/controllers/api/v0.6/create_jwk/schemas/jwk-response.schema.js +33 -0
- package/src/controllers/api/v0.6/credential-categories/controller.js +35 -0
- package/src/controllers/api/v0.6/devices/autohooks.js +11 -0
- package/src/controllers/api/v0.6/devices/controller.js +323 -0
- package/src/controllers/api/v0.6/devices/repo.js +27 -0
- package/src/controllers/api/v0.6/devices/schemas/device.schema.json +43 -0
- package/src/controllers/api/v0.6/devices/schemas/index.js +3 -0
- package/src/controllers/api/v0.6/feedback/controller.js +24 -0
- package/src/controllers/api/v0.6/feedback/schemas/index.js +3 -0
- package/src/controllers/api/v0.6/feedback/schemas/new-feedback.schema.js +47 -0
- package/src/controllers/api/v0.6/jwt/autohooks.js +10 -0
- package/src/controllers/api/v0.6/jwt/controller.js +44 -0
- package/src/controllers/api/v0.6/jwt/schemas/index.js +4 -0
- package/src/controllers/api/v0.6/jwt/schemas/jwt-request.schema.js +40 -0
- package/src/controllers/api/v0.6/jwt/schemas/jwt-response.schema.js +14 -0
- package/src/controllers/api/v0.6/oauth/controller.js +131 -0
- package/src/controllers/api/v0.6/push/controller.js +296 -0
- package/src/controllers/api/v0.6/push/firebase-initializer.js +21 -0
- package/src/controllers/api/v0.6/push/notification-types.js +37 -0
- package/src/controllers/api/v0.6/push/push-gateway-auth.js +123 -0
- package/src/controllers/api/v0.6/push/repo.js +24 -0
- package/src/controllers/api/v0.6/verification-offers/repo.js +25 -0
- package/src/controllers/api/v0.6/verify/autohooks.js +15 -0
- package/src/controllers/api/v0.6/verify/controller.js +348 -0
- package/src/controllers/api/v0.6/verify/repo.js +23 -0
- package/src/controllers/api/v0.6/verify/verification-credential-types.js +6 -0
- package/src/controllers/jwt/autohooks.js +10 -0
- package/src/controllers/jwt/controller.js +106 -0
- package/src/controllers/jwt/schemas/index.js +31 -0
- package/src/controllers/jwt/schemas/jwt-decode.response.200.schema.json +20 -0
- package/src/controllers/jwt/schemas/jwt-decode.schema.json +15 -0
- package/src/controllers/jwt/schemas/jwt-sign.response.200.schema.json +14 -0
- package/src/controllers/jwt/schemas/jwt-sign.schema.json +40 -0
- package/src/controllers/jwt/schemas/jwt-verify.response.200.schema.json +17 -0
- package/src/controllers/jwt/schemas/jwt-verify.schema.json +19 -0
- package/src/controllers/reference/autohooks.js +5 -0
- package/src/controllers/reference/countries/controller.js +81 -0
- package/src/controllers/reference/personas/controller.js +91 -0
- package/src/controllers/reference/personas/repo.js +29 -0
- package/src/controllers/reference/personas/schemas/index.js +3 -0
- package/src/controllers/reference/personas/schemas/persona.schema.json +108 -0
- package/src/controllers/root/controller.js +27 -0
- package/src/entities/accounts/constants.js +18 -0
- package/src/entities/accounts/domain/index.js +3 -0
- package/src/entities/accounts/domain/merge-scopes.js +9 -0
- package/src/entities/accounts/index.js +5 -0
- package/src/entities/accounts/repos/accounts.repo.js +64 -0
- package/src/entities/accounts/repos/id-token-claims-extension.js +31 -0
- package/src/entities/accounts/repos/index.js +3 -0
- package/src/entities/devices/constants.js +7 -0
- package/src/entities/devices/index.js +3 -0
- package/src/entities/feedback/domain/generate-feedback-email.js +23 -0
- package/src/entities/feedback/domain/index.js +3 -0
- package/src/entities/feedback/index.js +3 -0
- package/src/entities/index.js +9 -0
- package/src/entities/key-pairs/index.js +4 -0
- package/src/entities/key-pairs/orchestrators/generate-jwk.js +28 -0
- package/src/entities/key-pairs/orchestrators/get-key-pair.js +58 -0
- package/src/entities/key-pairs/orchestrators/index.js +4 -0
- package/src/entities/key-pairs/repos/index.js +3 -0
- package/src/entities/key-pairs/repos/key-pairs.repo.js +23 -0
- package/src/entities/oauth/constants.js +13 -0
- package/src/entities/oauth/domain/build-access-token.js +14 -0
- package/src/entities/oauth/domain/index.js +10 -0
- package/src/entities/oauth/domain/validate-audience.js +13 -0
- package/src/entities/oauth/domain/validate-client-id.js +13 -0
- package/src/entities/oauth/domain/validate-credential.js +16 -0
- package/src/entities/oauth/domain/validate-presentation.js +17 -0
- package/src/entities/oauth/domain/validate-refresh-token.js +17 -0
- package/src/entities/oauth/domain/validate-scope.js +30 -0
- package/src/entities/oauth/domain/verify-presentation.js +24 -0
- package/src/entities/oauth/index.js +4 -0
- package/src/entities/pushes/domains/errors.js +11 -0
- package/src/entities/pushes/domains/index.js +3 -0
- package/src/entities/pushes/index.js +3 -0
- package/src/entities/verification-code-attempts/index.js +3 -0
- package/src/entities/verification-code-attempts/orchestrators/index.js +3 -0
- package/src/entities/verification-code-attempts/orchestrators/validate-verification-code-attempts.js +79 -0
- package/src/entities/verification-code-attempts/repo.js +99 -0
- package/src/entities/verifications/constants.js +8 -0
- package/src/entities/verifications/index.js +3 -0
- package/src/helpers/caching-constants.js +6 -0
- package/src/helpers/index.js +3 -0
- package/src/index.js +15 -0
- package/src/init-server.js +88 -0
- package/src/plugins/index.js +4 -0
- package/src/plugins/vcl-verification-version-plugin.js +10 -0
- package/src/plugins/verify-access-token-plugin.js +116 -0
- package/src/standalone.js +8 -0
- package/test/accounts-controller.test.js +893 -0
- package/test/accounts-repo.test.js +92 -0
- package/test/careerwallet-config-controller.test.js +142 -0
- package/test/careerwallet-consents-controller.test.js +409 -0
- package/test/create_did_key-controller.test.js +397 -0
- package/test/create_jwk-controller.test.js +188 -0
- package/test/credential-categories-controller.test.js +29 -0
- package/test/credential-icons-controller.test.js +36 -0
- package/test/devices-controller.test.js +1025 -0
- package/test/factories/accounts-factory.js +15 -0
- package/test/factories/career-wallet-config-factory.js +60 -0
- package/test/factories/consents-career-wallet-factory.js +21 -0
- package/test/factories/devices-factory.js +16 -0
- package/test/factories/key-pairs-factory.js +18 -0
- package/test/factories/notifications-factory.js +16 -0
- package/test/factories/persona-factory.js +17 -0
- package/test/factories/refresh-tokens-factory.js +14 -0
- package/test/factories/verification-code-attempts-factory.js +17 -0
- package/test/factories/verification-factory.js +15 -0
- package/test/factories/verification-offer-factory.js +20 -0
- package/test/feedback-controller.test.js +225 -0
- package/test/helpers/.env.test +39 -0
- package/test/helpers/access-token.js +59 -0
- package/test/helpers/careerwallet-build-fastify.js +20 -0
- package/test/helpers/yoti.js +96 -0
- package/test/id-verification-controller.test.js +27 -0
- package/test/jwt-controller.test.js +1519 -0
- package/test/oauth-controller.test.js +639 -0
- package/test/push-controller.test.js +733 -0
- package/test/push-gateway-auth.test.js +208 -0
- package/test/reference-countries-controller.test.js +45 -0
- package/test/reference-personas-controller.test.js +179 -0
- package/test/root.test.js +21 -0
- package/test/swagger.test.js +21 -0
- package/test/verification-controller.test.js +1372 -0
|
@@ -0,0 +1,1519 @@
|
|
|
1
|
+
const AWS = require('aws-sdk');
|
|
2
|
+
const { ObjectId } = require('mongodb');
|
|
3
|
+
const { omit } = require('lodash/fp');
|
|
4
|
+
const { nanoid } = require('nanoid');
|
|
5
|
+
const { generateKeyPair } = require('@velocitycareerlabs/crypto');
|
|
6
|
+
const {
|
|
7
|
+
jwtSign,
|
|
8
|
+
jwtDecode,
|
|
9
|
+
generateDocJwt,
|
|
10
|
+
hexFromJwk,
|
|
11
|
+
jwtVerify,
|
|
12
|
+
verifyPresentationJwt,
|
|
13
|
+
stringifyJwk,
|
|
14
|
+
jwkFromStringified,
|
|
15
|
+
} = require('@velocitycareerlabs/jwt');
|
|
16
|
+
const { getDidUriFromJwk } = require('@velocitycareerlabs/did-doc');
|
|
17
|
+
const {
|
|
18
|
+
generateKeyPairInHexAndJwk,
|
|
19
|
+
errorResponseMatcher,
|
|
20
|
+
} = require('@velocitycareerlabs/tests-helpers');
|
|
21
|
+
const { mongoDb } = require('@spencejs/spence-mongo-repos');
|
|
22
|
+
const { formatISODuration } = require('date-fns');
|
|
23
|
+
const buildFastify = require('./helpers/careerwallet-build-fastify');
|
|
24
|
+
const initKeyPairsRepo = require('../src/entities/key-pairs/repos/key-pairs.repo');
|
|
25
|
+
const initDevicesFactory = require('./factories/devices-factory');
|
|
26
|
+
const keyPairsFactory = require('./factories/key-pairs-factory');
|
|
27
|
+
const accountsFactory = require('./factories/accounts-factory');
|
|
28
|
+
const {
|
|
29
|
+
missingAccessTokenExpectation,
|
|
30
|
+
incorrectAccessTokenExpectation,
|
|
31
|
+
incorrectScopeExpectation,
|
|
32
|
+
} = require('./helpers/access-token');
|
|
33
|
+
|
|
34
|
+
const expectedHeader = {
|
|
35
|
+
header: {
|
|
36
|
+
alg: 'ES256K',
|
|
37
|
+
jwk: {
|
|
38
|
+
crv: 'secp256k1',
|
|
39
|
+
kty: 'EC',
|
|
40
|
+
use: 'sig',
|
|
41
|
+
x: expect.any(String),
|
|
42
|
+
y: expect.any(String),
|
|
43
|
+
},
|
|
44
|
+
typ: 'JWT',
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const expected400ResponsePayloadStatusCodeAndError = {
|
|
49
|
+
statusCode: 400,
|
|
50
|
+
error: 'Bad Request',
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const buildUrl = () => '/jwt';
|
|
54
|
+
describe('JWT Controller Test Suite', () => {
|
|
55
|
+
const aliasName = 'alias/my-key-alias';
|
|
56
|
+
|
|
57
|
+
let fastify;
|
|
58
|
+
|
|
59
|
+
let privateKey;
|
|
60
|
+
let privateJwk;
|
|
61
|
+
let publicJwk;
|
|
62
|
+
|
|
63
|
+
let persistKeyPairs;
|
|
64
|
+
let persistDevices;
|
|
65
|
+
let persistAccounts;
|
|
66
|
+
|
|
67
|
+
let testClient;
|
|
68
|
+
let keyPairsRepo;
|
|
69
|
+
|
|
70
|
+
let validBearer;
|
|
71
|
+
let withoutScopeBearer;
|
|
72
|
+
|
|
73
|
+
let privk;
|
|
74
|
+
let publicKey;
|
|
75
|
+
|
|
76
|
+
beforeAll(async () => {
|
|
77
|
+
testClient = new AWS.KMS({
|
|
78
|
+
credentials: new AWS.Credentials('tests-kei-id', 'tests-key'),
|
|
79
|
+
region: 'us-west-1',
|
|
80
|
+
endpoint: 'http://localhost:4566',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const createKeyResponse = await testClient.createKey().promise();
|
|
84
|
+
const createAliasParams = {
|
|
85
|
+
AliasName: aliasName,
|
|
86
|
+
TargetKeyId: createKeyResponse.KeyMetadata.Arn,
|
|
87
|
+
};
|
|
88
|
+
await testClient.createAlias(createAliasParams).promise();
|
|
89
|
+
({ publicKey, privateKey: privk } = generateKeyPair({ format: 'jwk' }));
|
|
90
|
+
fastify = await buildFastify({
|
|
91
|
+
holderAppServerAccessTokenPublicKey: publicKey,
|
|
92
|
+
holderAppServerAccessTokenSigningKey: hexFromJwk(privk),
|
|
93
|
+
awsRegion: 'us-west-1',
|
|
94
|
+
awsEndpoint: 'http://localhost:4566',
|
|
95
|
+
oauthVerificationDisabledEndpoints: [
|
|
96
|
+
'POST:/jwt/verify',
|
|
97
|
+
'POST:/jwt/decode',
|
|
98
|
+
'POST:/jwt/sign',
|
|
99
|
+
],
|
|
100
|
+
managedAccountsKeyId: aliasName,
|
|
101
|
+
});
|
|
102
|
+
await fastify.ready();
|
|
103
|
+
({ privateKey, privateJwk, publicJwk } = generateKeyPairInHexAndJwk());
|
|
104
|
+
({ persistKeyPairs } = keyPairsFactory(fastify));
|
|
105
|
+
({ persistDevices } = initDevicesFactory(fastify));
|
|
106
|
+
({ persistAccounts } = accountsFactory(fastify));
|
|
107
|
+
keyPairsRepo = initKeyPairsRepo(fastify)({
|
|
108
|
+
log: fastify.log,
|
|
109
|
+
config: fastify.config,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
validBearer = await generateDocJwt(
|
|
113
|
+
{ scope: 'jwt test key:create', sub: 'abc' },
|
|
114
|
+
privk
|
|
115
|
+
);
|
|
116
|
+
withoutScopeBearer = await generateDocJwt(
|
|
117
|
+
{ scope: 'test', sub: 'abc' },
|
|
118
|
+
privk
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
beforeEach(async () => {
|
|
123
|
+
await mongoDb().collection('keyPairs').deleteMany({});
|
|
124
|
+
await mongoDb().collection('accounts').deleteMany({});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
afterAll(async () => {
|
|
128
|
+
await testClient.deleteAlias({ AliasName: aliasName }).promise();
|
|
129
|
+
await fastify.close();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('Access Token verification disabled', () => {
|
|
133
|
+
describe('/jwt', () => {
|
|
134
|
+
describe('JWT Decoding Tests', () => {
|
|
135
|
+
it('should decode a jwt', async () => {
|
|
136
|
+
const obj = {
|
|
137
|
+
field1: 'value1',
|
|
138
|
+
};
|
|
139
|
+
const jwtString = await generateDocJwt(obj, privateKey);
|
|
140
|
+
const response = await fastify.injectJson({
|
|
141
|
+
method: 'POST',
|
|
142
|
+
url: `${buildUrl()}/decode`,
|
|
143
|
+
payload: {
|
|
144
|
+
jwt: jwtString,
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
expect(response.statusCode).toEqual(200);
|
|
149
|
+
expect(response.json).toEqual({
|
|
150
|
+
payload: {
|
|
151
|
+
...obj,
|
|
152
|
+
iat: expect.any(Number),
|
|
153
|
+
nbf: expect.any(Number),
|
|
154
|
+
},
|
|
155
|
+
...expectedHeader,
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should fail to decode a malformed jwt', async () => {
|
|
160
|
+
const obj = {
|
|
161
|
+
field1: 'value1',
|
|
162
|
+
};
|
|
163
|
+
const jwtString = await generateDocJwt(obj, privateKey);
|
|
164
|
+
const malformedJwtString = `asd${jwtString}`;
|
|
165
|
+
|
|
166
|
+
const response = await fastify.injectJson({
|
|
167
|
+
method: 'POST',
|
|
168
|
+
url: `${buildUrl()}/decode`,
|
|
169
|
+
payload: {
|
|
170
|
+
jwt: malformedJwtString,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
expect(response.statusCode).toEqual(500);
|
|
174
|
+
expect(response.json).toEqual(
|
|
175
|
+
errorResponseMatcher({
|
|
176
|
+
statusCode: 500,
|
|
177
|
+
error: 'Internal Server Error',
|
|
178
|
+
message: 'Invalid Token or Protected Header formatting',
|
|
179
|
+
errorCode: 'missing_error_code',
|
|
180
|
+
})
|
|
181
|
+
);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
describe('JWT Signing Tests', () => {
|
|
186
|
+
it('should sign a jwt with options: issuer, audience, subject, expiresIn', async () => {
|
|
187
|
+
const issuerValue = nanoid();
|
|
188
|
+
const audienceValue = nanoid();
|
|
189
|
+
const subjectValue = nanoid();
|
|
190
|
+
const durationObj = { days: 7 };
|
|
191
|
+
const expiresInValue = formatISODuration(durationObj);
|
|
192
|
+
const obj = {
|
|
193
|
+
field1: 'value1',
|
|
194
|
+
};
|
|
195
|
+
const options = {
|
|
196
|
+
issuer: issuerValue,
|
|
197
|
+
audience: audienceValue,
|
|
198
|
+
subject: subjectValue,
|
|
199
|
+
expiresIn: expiresInValue,
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const response = await fastify.injectJson({
|
|
203
|
+
method: 'POST',
|
|
204
|
+
url: `${buildUrl()}/sign`,
|
|
205
|
+
payload: {
|
|
206
|
+
payload: obj,
|
|
207
|
+
options,
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
expect(response.statusCode).toEqual(200);
|
|
211
|
+
const decodedJwt = jwtDecode(response.json.jwt);
|
|
212
|
+
expect(decodedJwt).toEqual({
|
|
213
|
+
payload: {
|
|
214
|
+
...obj,
|
|
215
|
+
sub: subjectValue,
|
|
216
|
+
aud: audienceValue,
|
|
217
|
+
iss: issuerValue,
|
|
218
|
+
jti: expect.any(String),
|
|
219
|
+
iat: expect.any(Number),
|
|
220
|
+
nbf: expect.any(Number),
|
|
221
|
+
exp: expect.any(Number),
|
|
222
|
+
},
|
|
223
|
+
...expectedHeader,
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('should sign a jwt with options: issuer, audience, subject', async () => {
|
|
228
|
+
const issuerValue = nanoid();
|
|
229
|
+
const audienceValue = nanoid();
|
|
230
|
+
const subjectValue = nanoid();
|
|
231
|
+
const obj = {
|
|
232
|
+
field1: 'value1',
|
|
233
|
+
};
|
|
234
|
+
const options = {
|
|
235
|
+
issuer: issuerValue,
|
|
236
|
+
audience: audienceValue,
|
|
237
|
+
subject: subjectValue,
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const response = await fastify.injectJson({
|
|
241
|
+
method: 'POST',
|
|
242
|
+
url: `${buildUrl()}/sign`,
|
|
243
|
+
payload: {
|
|
244
|
+
payload: obj,
|
|
245
|
+
options,
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
expect(response.statusCode).toEqual(200);
|
|
249
|
+
const decodedJwt = jwtDecode(response.json.jwt);
|
|
250
|
+
expect(decodedJwt).toEqual({
|
|
251
|
+
payload: {
|
|
252
|
+
...obj,
|
|
253
|
+
sub: subjectValue,
|
|
254
|
+
aud: audienceValue,
|
|
255
|
+
iss: issuerValue,
|
|
256
|
+
jti: expect.any(String),
|
|
257
|
+
iat: expect.any(Number),
|
|
258
|
+
nbf: expect.any(Number),
|
|
259
|
+
exp: undefined,
|
|
260
|
+
},
|
|
261
|
+
...expectedHeader,
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('should sign a jwt with options: issuer, subject, expiresIn', async () => {
|
|
266
|
+
const durationObj = { days: 7 };
|
|
267
|
+
const issuerValue = nanoid();
|
|
268
|
+
const subjectValue = nanoid();
|
|
269
|
+
const expiresInValue = formatISODuration(durationObj);
|
|
270
|
+
const obj = {
|
|
271
|
+
field1: 'value1',
|
|
272
|
+
};
|
|
273
|
+
const options = {
|
|
274
|
+
issuer: issuerValue,
|
|
275
|
+
subject: subjectValue,
|
|
276
|
+
expiresIn: expiresInValue,
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const response = await fastify.injectJson({
|
|
280
|
+
method: 'POST',
|
|
281
|
+
url: `${buildUrl()}/sign`,
|
|
282
|
+
payload: {
|
|
283
|
+
payload: obj,
|
|
284
|
+
options,
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
expect(response.statusCode).toEqual(200);
|
|
288
|
+
const decodedJwt = jwtDecode(response.json.jwt);
|
|
289
|
+
expect(decodedJwt).toEqual({
|
|
290
|
+
payload: {
|
|
291
|
+
...obj,
|
|
292
|
+
sub: subjectValue,
|
|
293
|
+
aud: undefined,
|
|
294
|
+
iss: issuerValue,
|
|
295
|
+
jti: expect.any(String),
|
|
296
|
+
iat: expect.any(Number),
|
|
297
|
+
nbf: expect.any(Number),
|
|
298
|
+
exp: expect.any(Number),
|
|
299
|
+
},
|
|
300
|
+
...expectedHeader,
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('should sign a jwt with just the required options: issuer and subject', async () => {
|
|
305
|
+
const issuerValue = nanoid();
|
|
306
|
+
const subjectValue = nanoid();
|
|
307
|
+
const obj = {
|
|
308
|
+
field1: 'value1',
|
|
309
|
+
};
|
|
310
|
+
const options = {
|
|
311
|
+
issuer: issuerValue,
|
|
312
|
+
subject: subjectValue,
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const response = await fastify.injectJson({
|
|
316
|
+
method: 'POST',
|
|
317
|
+
url: `${buildUrl()}/sign`,
|
|
318
|
+
payload: {
|
|
319
|
+
payload: obj,
|
|
320
|
+
options,
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
expect(response.statusCode).toEqual(200);
|
|
324
|
+
const decodedJwt = jwtDecode(response.json.jwt);
|
|
325
|
+
expect(decodedJwt).toEqual({
|
|
326
|
+
payload: {
|
|
327
|
+
...obj,
|
|
328
|
+
sub: subjectValue,
|
|
329
|
+
aud: undefined,
|
|
330
|
+
iss: issuerValue,
|
|
331
|
+
jti: expect.any(String),
|
|
332
|
+
iat: expect.any(Number),
|
|
333
|
+
nbf: expect.any(Number),
|
|
334
|
+
exp: undefined,
|
|
335
|
+
},
|
|
336
|
+
...expectedHeader,
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('should fail to sign with missing required option: subject', async () => {
|
|
341
|
+
const issuerValue = nanoid();
|
|
342
|
+
const obj = {
|
|
343
|
+
field1: 'value1',
|
|
344
|
+
};
|
|
345
|
+
const options = {
|
|
346
|
+
issuer: issuerValue,
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const response = await fastify.injectJson({
|
|
350
|
+
method: 'POST',
|
|
351
|
+
url: `${buildUrl()}/sign`,
|
|
352
|
+
payload: {
|
|
353
|
+
payload: obj,
|
|
354
|
+
options,
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
expect(response.statusCode).toEqual(400);
|
|
358
|
+
expect(response.json).toEqual(
|
|
359
|
+
errorResponseMatcher({
|
|
360
|
+
...expected400ResponsePayloadStatusCodeAndError,
|
|
361
|
+
message: "body/options must have required property 'subject'",
|
|
362
|
+
errorCode: 'request_validation_failed',
|
|
363
|
+
})
|
|
364
|
+
);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('should fail to sign with missing required option: issuer', async () => {
|
|
368
|
+
const subjectValue = nanoid();
|
|
369
|
+
const obj = {
|
|
370
|
+
field1: 'value1',
|
|
371
|
+
};
|
|
372
|
+
const options = {
|
|
373
|
+
subject: subjectValue,
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const response = await fastify.injectJson({
|
|
377
|
+
method: 'POST',
|
|
378
|
+
url: `${buildUrl()}/sign`,
|
|
379
|
+
payload: {
|
|
380
|
+
payload: obj,
|
|
381
|
+
options,
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
expect(response.statusCode).toEqual(400);
|
|
385
|
+
expect(response.json).toEqual(
|
|
386
|
+
errorResponseMatcher({
|
|
387
|
+
...expected400ResponsePayloadStatusCodeAndError,
|
|
388
|
+
message: "body/options must have required property 'issuer'",
|
|
389
|
+
errorCode: 'request_validation_failed',
|
|
390
|
+
})
|
|
391
|
+
);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it('should fail to sign a jwt with no options', async () => {
|
|
395
|
+
const obj = {
|
|
396
|
+
field1: 'value1',
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const response = await fastify.injectJson({
|
|
400
|
+
method: 'POST',
|
|
401
|
+
url: `${buildUrl()}/sign`,
|
|
402
|
+
payload: {
|
|
403
|
+
payload: obj,
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
expect(response.statusCode).toEqual(400);
|
|
407
|
+
expect(response.json).toEqual(
|
|
408
|
+
errorResponseMatcher({
|
|
409
|
+
...expected400ResponsePayloadStatusCodeAndError,
|
|
410
|
+
message: "body must have required property 'options'",
|
|
411
|
+
errorCode: 'request_validation_failed',
|
|
412
|
+
})
|
|
413
|
+
);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it('should fail to sign with missing payload', async () => {
|
|
417
|
+
const response = await fastify.injectJson({
|
|
418
|
+
method: 'POST',
|
|
419
|
+
url: `${buildUrl()}/sign`,
|
|
420
|
+
payload: {},
|
|
421
|
+
});
|
|
422
|
+
expect(response.statusCode).toEqual(400);
|
|
423
|
+
expect(response.json).toEqual(
|
|
424
|
+
errorResponseMatcher({
|
|
425
|
+
...expected400ResponsePayloadStatusCodeAndError,
|
|
426
|
+
message: "body must have required property 'payload'",
|
|
427
|
+
errorCode: 'request_validation_failed',
|
|
428
|
+
})
|
|
429
|
+
);
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
describe('JWT Verification Tests', () => {
|
|
434
|
+
it('should verify a jwt using an explicit public key', async () => {
|
|
435
|
+
const obj = {
|
|
436
|
+
field1: 'value1',
|
|
437
|
+
};
|
|
438
|
+
const jwt = await jwtSign(obj, privateJwk);
|
|
439
|
+
|
|
440
|
+
const response = await fastify.injectJson({
|
|
441
|
+
method: 'POST',
|
|
442
|
+
url: `${buildUrl()}/verify`,
|
|
443
|
+
payload: {
|
|
444
|
+
jwt,
|
|
445
|
+
publicKey: publicJwk,
|
|
446
|
+
},
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
expect(response.statusCode).toEqual(200);
|
|
450
|
+
expect(response.json).toEqual({
|
|
451
|
+
verified: true,
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it('should verify a jwt using a public key as a jwt header jwk', async () => {
|
|
456
|
+
const obj = {
|
|
457
|
+
field1: 'value1',
|
|
458
|
+
};
|
|
459
|
+
const jwt = await generateDocJwt(obj, privateKey);
|
|
460
|
+
|
|
461
|
+
const response = await fastify.injectJson({
|
|
462
|
+
method: 'POST',
|
|
463
|
+
url: `${buildUrl()}/verify`,
|
|
464
|
+
payload: {
|
|
465
|
+
jwt,
|
|
466
|
+
},
|
|
467
|
+
});
|
|
468
|
+
expect(response.statusCode).toEqual(200);
|
|
469
|
+
expect(response.json).toEqual({
|
|
470
|
+
verified: true,
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
it('should fail to verify a jwt using the wrong public key', async () => {
|
|
475
|
+
const obj = {
|
|
476
|
+
field1: 'value1',
|
|
477
|
+
};
|
|
478
|
+
const { publicJwk: wrongPublicJwk } = generateKeyPairInHexAndJwk();
|
|
479
|
+
const jwt = await jwtSign(obj, privateJwk);
|
|
480
|
+
|
|
481
|
+
const response = await fastify.injectJson({
|
|
482
|
+
method: 'POST',
|
|
483
|
+
url: `${buildUrl()}/verify`,
|
|
484
|
+
payload: {
|
|
485
|
+
jwt,
|
|
486
|
+
publicKey: wrongPublicJwk,
|
|
487
|
+
},
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
expect(response.json).toEqual({
|
|
491
|
+
verified: false,
|
|
492
|
+
error:
|
|
493
|
+
'JWSSignatureVerificationFailed: signature verification failed',
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it('verification should 400 with no public key', async () => {
|
|
498
|
+
const obj = {
|
|
499
|
+
field1: 'value1',
|
|
500
|
+
};
|
|
501
|
+
const jwt = await jwtSign(obj, privateJwk);
|
|
502
|
+
|
|
503
|
+
const response = await fastify.injectJson({
|
|
504
|
+
method: 'POST',
|
|
505
|
+
url: `${buildUrl()}/verify`,
|
|
506
|
+
payload: {
|
|
507
|
+
jwt,
|
|
508
|
+
},
|
|
509
|
+
});
|
|
510
|
+
expect(response.statusCode).toEqual(400);
|
|
511
|
+
expect(response.json).toEqual(
|
|
512
|
+
errorResponseMatcher({
|
|
513
|
+
...expected400ResponsePayloadStatusCodeAndError,
|
|
514
|
+
message: 'Public key is missing',
|
|
515
|
+
errorCode: 'missing_error_code',
|
|
516
|
+
})
|
|
517
|
+
);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
it('verification should 200 with an error with a malformed public key', async () => {
|
|
521
|
+
const obj = {
|
|
522
|
+
field1: 'value1',
|
|
523
|
+
};
|
|
524
|
+
const jwt = await jwtSign(obj, privateJwk);
|
|
525
|
+
const response = await fastify.injectJson({
|
|
526
|
+
method: 'POST',
|
|
527
|
+
url: `${buildUrl()}/verify`,
|
|
528
|
+
payload: {
|
|
529
|
+
jwt,
|
|
530
|
+
publicKey: omit('x', publicJwk),
|
|
531
|
+
},
|
|
532
|
+
});
|
|
533
|
+
expect(response.statusCode).toEqual(200);
|
|
534
|
+
expect(response.json).toEqual({
|
|
535
|
+
verified: false,
|
|
536
|
+
error: expect.any(String),
|
|
537
|
+
});
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
describe('Access Token verification enabled', () => {
|
|
544
|
+
beforeAll(async () => {
|
|
545
|
+
fastify.overrides.reqConfig = (config) => ({
|
|
546
|
+
...config,
|
|
547
|
+
oauthVerificationDisabledEndpoints: [], // enabled for all endpoints
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
describe('/api/v0.6/jwt', () => {
|
|
552
|
+
const api = '/api/v0.6/jwt';
|
|
553
|
+
|
|
554
|
+
describe('sign jwt test suite', () => {
|
|
555
|
+
it('should fail to sign a jwt with missing payload in body', async () => {
|
|
556
|
+
const response = await fastify.injectJson({
|
|
557
|
+
method: 'POST',
|
|
558
|
+
url: `${api}/sign`,
|
|
559
|
+
payload: {
|
|
560
|
+
header: {},
|
|
561
|
+
},
|
|
562
|
+
headers: {
|
|
563
|
+
authorization: `Bearer ${validBearer}`,
|
|
564
|
+
},
|
|
565
|
+
});
|
|
566
|
+
expect(response.statusCode).toEqual(400);
|
|
567
|
+
expect(response.json).toEqual(
|
|
568
|
+
errorResponseMatcher({
|
|
569
|
+
error: 'Bad Request',
|
|
570
|
+
errorCode: 'request_validation_failed',
|
|
571
|
+
message: "body must have required property 'payload'",
|
|
572
|
+
statusCode: 400,
|
|
573
|
+
})
|
|
574
|
+
);
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
it('should fail to sign a jwt with empty keyId in options', async () => {
|
|
578
|
+
const response = await fastify.injectJson({
|
|
579
|
+
method: 'POST',
|
|
580
|
+
url: `${api}/sign`,
|
|
581
|
+
payload: {
|
|
582
|
+
header: {},
|
|
583
|
+
payload: {},
|
|
584
|
+
options: {
|
|
585
|
+
keyId: '',
|
|
586
|
+
},
|
|
587
|
+
},
|
|
588
|
+
headers: {
|
|
589
|
+
authorization: `Bearer ${validBearer}`,
|
|
590
|
+
},
|
|
591
|
+
});
|
|
592
|
+
expect(response.statusCode).toEqual(400);
|
|
593
|
+
expect(response.json).toEqual(
|
|
594
|
+
errorResponseMatcher({
|
|
595
|
+
error: 'Bad Request',
|
|
596
|
+
errorCode: 'request_validation_failed',
|
|
597
|
+
message:
|
|
598
|
+
// eslint-disable-next-line max-len
|
|
599
|
+
"body/options must have required property 'kid', body/options/keyId must NOT have fewer than 1 characters, body/options must match exactly one schema in oneOf",
|
|
600
|
+
statusCode: 400,
|
|
601
|
+
})
|
|
602
|
+
);
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
it('should fail to sign a jwt with empty kid in options', async () => {
|
|
606
|
+
const response = await fastify.injectJson({
|
|
607
|
+
method: 'POST',
|
|
608
|
+
url: `${api}/sign`,
|
|
609
|
+
payload: {
|
|
610
|
+
header: {},
|
|
611
|
+
payload: {},
|
|
612
|
+
options: {
|
|
613
|
+
kid: '',
|
|
614
|
+
},
|
|
615
|
+
},
|
|
616
|
+
headers: {
|
|
617
|
+
authorization: `Bearer ${validBearer}`,
|
|
618
|
+
},
|
|
619
|
+
});
|
|
620
|
+
expect(response.statusCode).toEqual(400);
|
|
621
|
+
expect(response.json).toEqual(
|
|
622
|
+
errorResponseMatcher({
|
|
623
|
+
error: 'Bad Request',
|
|
624
|
+
errorCode: 'request_validation_failed',
|
|
625
|
+
message:
|
|
626
|
+
// eslint-disable-next-line max-len
|
|
627
|
+
"body/options/kid must NOT have fewer than 1 characters, body/options must have required property 'keyId', body/options must match exactly one schema in oneOf",
|
|
628
|
+
statusCode: 400,
|
|
629
|
+
})
|
|
630
|
+
);
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
it('should fail to sign a jwt when no access token is provided', async () => {
|
|
634
|
+
const response = await fastify.injectJson({
|
|
635
|
+
method: 'POST',
|
|
636
|
+
url: `${api}/sign`,
|
|
637
|
+
payload: {
|
|
638
|
+
header: {},
|
|
639
|
+
payload: {},
|
|
640
|
+
options: {
|
|
641
|
+
keyId: 'abc',
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
missingAccessTokenExpectation(response);
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
it('should fail to sign a jwt when incorrect access token is provided', async () => {
|
|
650
|
+
const response = await fastify.injectJson({
|
|
651
|
+
method: 'POST',
|
|
652
|
+
url: `${api}/sign`,
|
|
653
|
+
payload: {
|
|
654
|
+
header: {},
|
|
655
|
+
|
|
656
|
+
payload: {},
|
|
657
|
+
options: {
|
|
658
|
+
keyId: 'abc',
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
headers: {
|
|
662
|
+
authorization: 'Bearer incorrect_access_token',
|
|
663
|
+
},
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
incorrectAccessTokenExpectation(response);
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
it('should fail to sign a jwt when incorrect scope is provided', async () => {
|
|
670
|
+
const response = await fastify.injectJson({
|
|
671
|
+
method: 'POST',
|
|
672
|
+
url: `${api}/sign`,
|
|
673
|
+
headers: {
|
|
674
|
+
authorization: `Bearer ${withoutScopeBearer}`,
|
|
675
|
+
},
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
incorrectScopeExpectation(response);
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
it('should fail when a key pair could not be found by kid', async () => {
|
|
682
|
+
const { publicKey: pubK } = generateKeyPair({
|
|
683
|
+
curve: 'P-256',
|
|
684
|
+
format: 'jwk',
|
|
685
|
+
});
|
|
686
|
+
const didJwk = getDidUriFromJwk(pubK);
|
|
687
|
+
const response = await fastify.injectJson({
|
|
688
|
+
method: 'POST',
|
|
689
|
+
url: `${api}/sign`,
|
|
690
|
+
payload: {
|
|
691
|
+
header: {},
|
|
692
|
+
payload: {
|
|
693
|
+
abc: 'abv',
|
|
694
|
+
},
|
|
695
|
+
options: {
|
|
696
|
+
kid: didJwk,
|
|
697
|
+
},
|
|
698
|
+
},
|
|
699
|
+
headers: {
|
|
700
|
+
Authorization: `Bearer ${validBearer}`,
|
|
701
|
+
},
|
|
702
|
+
});
|
|
703
|
+
expect(response.statusCode).toEqual(400);
|
|
704
|
+
expect(response.json).toEqual(
|
|
705
|
+
errorResponseMatcher({
|
|
706
|
+
error: 'Bad Request',
|
|
707
|
+
errorCode: 'invalid_kid',
|
|
708
|
+
message: 'Key pair not found',
|
|
709
|
+
statusCode: 400,
|
|
710
|
+
})
|
|
711
|
+
);
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
it('should fail when a key pair could not be found by keyId', async () => {
|
|
715
|
+
const response = await fastify.injectJson({
|
|
716
|
+
method: 'POST',
|
|
717
|
+
url: `${api}/sign`,
|
|
718
|
+
payload: {
|
|
719
|
+
header: {},
|
|
720
|
+
payload: {
|
|
721
|
+
abc: 'abv',
|
|
722
|
+
},
|
|
723
|
+
options: {
|
|
724
|
+
keyId: new ObjectId().toString(),
|
|
725
|
+
},
|
|
726
|
+
},
|
|
727
|
+
headers: {
|
|
728
|
+
Authorization: `Bearer ${validBearer}`,
|
|
729
|
+
},
|
|
730
|
+
});
|
|
731
|
+
expect(response.statusCode).toEqual(400);
|
|
732
|
+
expect(response.json).toEqual(
|
|
733
|
+
errorResponseMatcher({
|
|
734
|
+
error: 'Bad Request',
|
|
735
|
+
errorCode: 'invalid_key_id',
|
|
736
|
+
message: 'Key pair not found',
|
|
737
|
+
statusCode: 400,
|
|
738
|
+
})
|
|
739
|
+
);
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
it('should throw error if account does not have key', async () => {
|
|
743
|
+
const { publicKey: pubK, privateKey: pk } = generateKeyPair({
|
|
744
|
+
curve: 'P-256',
|
|
745
|
+
format: 'jwk',
|
|
746
|
+
});
|
|
747
|
+
const params = {
|
|
748
|
+
KeyId: fastify.config.managedAccountsKeyId,
|
|
749
|
+
Plaintext: stringifyJwk(pk),
|
|
750
|
+
};
|
|
751
|
+
const { CiphertextBlob } = await testClient.encrypt(params).promise();
|
|
752
|
+
const keyPairDb = await persistKeyPairs({
|
|
753
|
+
encryptedPrivateKey: CiphertextBlob,
|
|
754
|
+
publicKey: pubK,
|
|
755
|
+
});
|
|
756
|
+
const account = await persistAccounts({
|
|
757
|
+
didKeyMetadatum: [],
|
|
758
|
+
});
|
|
759
|
+
validBearer = await generateDocJwt(
|
|
760
|
+
{ scope: 'jwt test key:create', sub: account._id.toString() },
|
|
761
|
+
privk
|
|
762
|
+
);
|
|
763
|
+
const response1 = await fastify.injectJson({
|
|
764
|
+
method: 'POST',
|
|
765
|
+
url: `${api}/sign`,
|
|
766
|
+
payload: {
|
|
767
|
+
header: {},
|
|
768
|
+
payload: {
|
|
769
|
+
abc: 'abv',
|
|
770
|
+
},
|
|
771
|
+
options: {
|
|
772
|
+
keyId: keyPairDb._id.toString(),
|
|
773
|
+
},
|
|
774
|
+
},
|
|
775
|
+
headers: {
|
|
776
|
+
Authorization: `Bearer ${validBearer}`,
|
|
777
|
+
},
|
|
778
|
+
});
|
|
779
|
+
const response2 = await fastify.injectJson({
|
|
780
|
+
method: 'POST',
|
|
781
|
+
url: `${api}/sign`,
|
|
782
|
+
payload: {
|
|
783
|
+
header: {},
|
|
784
|
+
payload: {
|
|
785
|
+
abc: 'abv',
|
|
786
|
+
},
|
|
787
|
+
options: {
|
|
788
|
+
kid: getDidUriFromJwk(pubK),
|
|
789
|
+
},
|
|
790
|
+
},
|
|
791
|
+
headers: {
|
|
792
|
+
Authorization: `Bearer ${validBearer}`,
|
|
793
|
+
},
|
|
794
|
+
});
|
|
795
|
+
expect(response1.statusCode).toEqual(400);
|
|
796
|
+
expect(response1.json).toEqual(
|
|
797
|
+
errorResponseMatcher({
|
|
798
|
+
error: 'Bad Request',
|
|
799
|
+
errorCode: 'invalid_key_id',
|
|
800
|
+
message: 'Key pair not found',
|
|
801
|
+
statusCode: 400,
|
|
802
|
+
})
|
|
803
|
+
);
|
|
804
|
+
expect(response2.statusCode).toEqual(400);
|
|
805
|
+
expect(response2.json).toEqual(
|
|
806
|
+
errorResponseMatcher({
|
|
807
|
+
error: 'Bad Request',
|
|
808
|
+
errorCode: 'invalid_kid',
|
|
809
|
+
message: 'Key pair not found',
|
|
810
|
+
statusCode: 400,
|
|
811
|
+
})
|
|
812
|
+
);
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
it('should sign a jwt with keyId', async () => {
|
|
816
|
+
const { publicKey: pubK, privateKey: pk } = generateKeyPair({
|
|
817
|
+
curve: 'P-256',
|
|
818
|
+
format: 'jwk',
|
|
819
|
+
});
|
|
820
|
+
const params = {
|
|
821
|
+
KeyId: fastify.config.managedAccountsKeyId,
|
|
822
|
+
Plaintext: stringifyJwk(pk),
|
|
823
|
+
};
|
|
824
|
+
const { CiphertextBlob } = await testClient.encrypt(params).promise();
|
|
825
|
+
const keyPairDb = await persistKeyPairs({
|
|
826
|
+
encryptedPrivateKey: CiphertextBlob,
|
|
827
|
+
publicKey: pubK,
|
|
828
|
+
});
|
|
829
|
+
const account = await persistAccounts({
|
|
830
|
+
didKeyMetadatum: [
|
|
831
|
+
{
|
|
832
|
+
kid: getDidUriFromJwk(pubK),
|
|
833
|
+
keyId: new ObjectId(keyPairDb._id),
|
|
834
|
+
},
|
|
835
|
+
],
|
|
836
|
+
});
|
|
837
|
+
validBearer = await generateDocJwt(
|
|
838
|
+
{ scope: 'jwt test key:create', sub: account._id.toString() },
|
|
839
|
+
privk
|
|
840
|
+
);
|
|
841
|
+
const response = await fastify.injectJson({
|
|
842
|
+
method: 'POST',
|
|
843
|
+
url: `${api}/sign`,
|
|
844
|
+
payload: {
|
|
845
|
+
header: {},
|
|
846
|
+
payload: {
|
|
847
|
+
abc: 'abv',
|
|
848
|
+
},
|
|
849
|
+
options: {
|
|
850
|
+
keyId: keyPairDb._id.toString(),
|
|
851
|
+
},
|
|
852
|
+
},
|
|
853
|
+
headers: {
|
|
854
|
+
Authorization: `Bearer ${validBearer}`,
|
|
855
|
+
},
|
|
856
|
+
});
|
|
857
|
+
expect(response.statusCode).toEqual(200);
|
|
858
|
+
expect(response.json).toEqual({
|
|
859
|
+
compactJwt: expect.any(String),
|
|
860
|
+
});
|
|
861
|
+
const keyPair = await keyPairsRepo.findOne({
|
|
862
|
+
filter: {
|
|
863
|
+
_id: keyPairDb._id,
|
|
864
|
+
},
|
|
865
|
+
});
|
|
866
|
+
const decryptedPrivateKey = await testClient
|
|
867
|
+
.decrypt({
|
|
868
|
+
CiphertextBlob: Buffer.from(keyPair.encryptedPrivateKey.buffer),
|
|
869
|
+
})
|
|
870
|
+
.promise();
|
|
871
|
+
|
|
872
|
+
const { payload } = await jwtVerify(
|
|
873
|
+
response.json.compactJwt,
|
|
874
|
+
jwkFromStringified(decryptedPrivateKey.Plaintext.toString())
|
|
875
|
+
);
|
|
876
|
+
expect(payload.abc).toEqual('abv');
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
it('should sign a jwt with keyId of a specific account when token has admin scope', async () => {
|
|
880
|
+
const { publicKey: pubK, privateKey: pk } = generateKeyPair({
|
|
881
|
+
curve: 'P-256',
|
|
882
|
+
format: 'jwk',
|
|
883
|
+
});
|
|
884
|
+
const params = {
|
|
885
|
+
KeyId: fastify.config.managedAccountsKeyId,
|
|
886
|
+
Plaintext: stringifyJwk(pk),
|
|
887
|
+
};
|
|
888
|
+
const { CiphertextBlob } = await testClient.encrypt(params).promise();
|
|
889
|
+
const keyPairDb = await persistKeyPairs({
|
|
890
|
+
encryptedPrivateKey: CiphertextBlob,
|
|
891
|
+
publicKey: pubK,
|
|
892
|
+
});
|
|
893
|
+
await persistAccounts({
|
|
894
|
+
didKeyMetadatum: [
|
|
895
|
+
{
|
|
896
|
+
kid: getDidUriFromJwk(pubK),
|
|
897
|
+
keyId: new ObjectId(keyPairDb._id),
|
|
898
|
+
},
|
|
899
|
+
],
|
|
900
|
+
});
|
|
901
|
+
validBearer = await generateDocJwt(
|
|
902
|
+
{ scope: 'jwt test key:create admin:client', sub: 'not-the-user' },
|
|
903
|
+
privk
|
|
904
|
+
);
|
|
905
|
+
const response = await fastify.injectJson({
|
|
906
|
+
method: 'POST',
|
|
907
|
+
url: `${api}/sign`,
|
|
908
|
+
payload: {
|
|
909
|
+
header: {},
|
|
910
|
+
payload: {
|
|
911
|
+
abc: 'abv',
|
|
912
|
+
},
|
|
913
|
+
options: {
|
|
914
|
+
keyId: keyPairDb._id.toString(),
|
|
915
|
+
},
|
|
916
|
+
},
|
|
917
|
+
headers: {
|
|
918
|
+
Authorization: `Bearer ${validBearer}`,
|
|
919
|
+
},
|
|
920
|
+
});
|
|
921
|
+
expect(response.statusCode).toEqual(200);
|
|
922
|
+
expect(response.json).toEqual({
|
|
923
|
+
compactJwt: expect.any(String),
|
|
924
|
+
});
|
|
925
|
+
const keyPair = await keyPairsRepo.findOne({
|
|
926
|
+
filter: {
|
|
927
|
+
_id: keyPairDb._id,
|
|
928
|
+
},
|
|
929
|
+
});
|
|
930
|
+
const decryptedPrivateKey = await testClient
|
|
931
|
+
.decrypt({
|
|
932
|
+
CiphertextBlob: Buffer.from(keyPair.encryptedPrivateKey.buffer),
|
|
933
|
+
})
|
|
934
|
+
.promise();
|
|
935
|
+
|
|
936
|
+
const { payload } = await jwtVerify(
|
|
937
|
+
response.json.compactJwt,
|
|
938
|
+
jwkFromStringified(decryptedPrivateKey.Plaintext.toString())
|
|
939
|
+
);
|
|
940
|
+
expect(payload.abc).toEqual('abv');
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
it('should sign a jwt and verify P-256', async () => {
|
|
944
|
+
const payload = {
|
|
945
|
+
vp: {
|
|
946
|
+
presentation_submission: {
|
|
947
|
+
descriptor_map: [],
|
|
948
|
+
definition_id:
|
|
949
|
+
'65d4f928ffbbda0ea32d159c.634e5620fe258ef4580d3c63',
|
|
950
|
+
id: 'B424D9E8-4773-49EC-8B66-1D22EACBE848',
|
|
951
|
+
},
|
|
952
|
+
},
|
|
953
|
+
};
|
|
954
|
+
const { publicKey: pubK, privateKey: pk } = generateKeyPair({
|
|
955
|
+
curve: 'P-256',
|
|
956
|
+
format: 'jwk',
|
|
957
|
+
});
|
|
958
|
+
const params = {
|
|
959
|
+
KeyId: fastify.config.managedAccountsKeyId,
|
|
960
|
+
Plaintext: stringifyJwk(pk),
|
|
961
|
+
};
|
|
962
|
+
const { CiphertextBlob } = await testClient.encrypt(params).promise();
|
|
963
|
+
const keyPairDb = await persistKeyPairs({
|
|
964
|
+
encryptedPrivateKey: CiphertextBlob,
|
|
965
|
+
publicKey: pubK,
|
|
966
|
+
});
|
|
967
|
+
const account = await persistAccounts({
|
|
968
|
+
didKeyMetadatum: [
|
|
969
|
+
{
|
|
970
|
+
kid: getDidUriFromJwk(pubK),
|
|
971
|
+
keyId: new ObjectId(keyPairDb._id),
|
|
972
|
+
},
|
|
973
|
+
],
|
|
974
|
+
});
|
|
975
|
+
validBearer = await generateDocJwt(
|
|
976
|
+
{ scope: 'jwt test key:create', sub: account._id.toString() },
|
|
977
|
+
privk
|
|
978
|
+
);
|
|
979
|
+
const response = await fastify.injectJson({
|
|
980
|
+
method: 'POST',
|
|
981
|
+
url: `${api}/sign`,
|
|
982
|
+
payload: {
|
|
983
|
+
header: {
|
|
984
|
+
jwk: pubK,
|
|
985
|
+
},
|
|
986
|
+
payload,
|
|
987
|
+
options: {
|
|
988
|
+
keyId: keyPairDb._id.toString(),
|
|
989
|
+
},
|
|
990
|
+
},
|
|
991
|
+
headers: {
|
|
992
|
+
Authorization: `Bearer ${validBearer}`,
|
|
993
|
+
},
|
|
994
|
+
});
|
|
995
|
+
expect(response.statusCode).toEqual(200);
|
|
996
|
+
expect(response.json).toEqual({
|
|
997
|
+
compactJwt: expect.any(String),
|
|
998
|
+
});
|
|
999
|
+
const presentation = await verifyPresentationJwt(
|
|
1000
|
+
response.json.compactJwt
|
|
1001
|
+
);
|
|
1002
|
+
expect(presentation.presentation_submission).toEqual(
|
|
1003
|
+
payload.vp.presentation_submission
|
|
1004
|
+
);
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
it('should sign a jwt and verify secp256k1', async () => {
|
|
1008
|
+
const payload = {
|
|
1009
|
+
vp: {
|
|
1010
|
+
presentation_submission: {
|
|
1011
|
+
descriptor_map: [],
|
|
1012
|
+
definition_id:
|
|
1013
|
+
'65d4f928ffbbda0ea32d159c.634e5620fe258ef4580d3c63',
|
|
1014
|
+
id: 'B424D9E8-4773-49EC-8B66-1D22EACBE848',
|
|
1015
|
+
},
|
|
1016
|
+
},
|
|
1017
|
+
};
|
|
1018
|
+
const { publicKey: pubK, privateKey: pk } = generateKeyPair({
|
|
1019
|
+
curve: 'secp256k1',
|
|
1020
|
+
format: 'jwk',
|
|
1021
|
+
});
|
|
1022
|
+
const params = {
|
|
1023
|
+
KeyId: fastify.config.managedAccountsKeyId,
|
|
1024
|
+
Plaintext: stringifyJwk(pk),
|
|
1025
|
+
};
|
|
1026
|
+
const { CiphertextBlob } = await testClient.encrypt(params).promise();
|
|
1027
|
+
const keyPairDb = await persistKeyPairs({
|
|
1028
|
+
encryptedPrivateKey: CiphertextBlob,
|
|
1029
|
+
publicKey: pubK,
|
|
1030
|
+
});
|
|
1031
|
+
const account = await persistAccounts({
|
|
1032
|
+
didKeyMetadatum: [
|
|
1033
|
+
{
|
|
1034
|
+
kid: getDidUriFromJwk(pubK),
|
|
1035
|
+
keyId: new ObjectId(keyPairDb._id),
|
|
1036
|
+
},
|
|
1037
|
+
],
|
|
1038
|
+
});
|
|
1039
|
+
validBearer = await generateDocJwt(
|
|
1040
|
+
{ scope: 'jwt test key:create', sub: account._id.toString() },
|
|
1041
|
+
privk
|
|
1042
|
+
);
|
|
1043
|
+
const response = await fastify.injectJson({
|
|
1044
|
+
method: 'POST',
|
|
1045
|
+
url: `${api}/sign`,
|
|
1046
|
+
payload: {
|
|
1047
|
+
header: {
|
|
1048
|
+
jwk: pubK,
|
|
1049
|
+
},
|
|
1050
|
+
payload,
|
|
1051
|
+
options: {
|
|
1052
|
+
keyId: keyPairDb._id.toString(),
|
|
1053
|
+
},
|
|
1054
|
+
},
|
|
1055
|
+
headers: {
|
|
1056
|
+
Authorization: `Bearer ${validBearer}`,
|
|
1057
|
+
},
|
|
1058
|
+
});
|
|
1059
|
+
expect(response.statusCode).toEqual(200);
|
|
1060
|
+
expect(response.json).toEqual({
|
|
1061
|
+
compactJwt: expect.any(String),
|
|
1062
|
+
});
|
|
1063
|
+
const presentation = await verifyPresentationJwt(
|
|
1064
|
+
response.json.compactJwt
|
|
1065
|
+
);
|
|
1066
|
+
expect(presentation.presentation_submission).toEqual(
|
|
1067
|
+
payload.vp.presentation_submission
|
|
1068
|
+
);
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
it('should sign a jwt with kid', async () => {
|
|
1072
|
+
const { publicKey: pubK, privateKey: pk } = generateKeyPair({
|
|
1073
|
+
curve: 'P-256',
|
|
1074
|
+
format: 'jwk',
|
|
1075
|
+
});
|
|
1076
|
+
const params = {
|
|
1077
|
+
KeyId: fastify.config.managedAccountsKeyId,
|
|
1078
|
+
Plaintext: stringifyJwk(pk),
|
|
1079
|
+
};
|
|
1080
|
+
const { CiphertextBlob } = await testClient.encrypt(params).promise();
|
|
1081
|
+
const keyPairDb = await persistKeyPairs({
|
|
1082
|
+
encryptedPrivateKey: CiphertextBlob,
|
|
1083
|
+
publicKey: pubK,
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1086
|
+
const didJwk = getDidUriFromJwk(pubK);
|
|
1087
|
+
const account = await persistAccounts({
|
|
1088
|
+
didKeyMetadatum: [
|
|
1089
|
+
{
|
|
1090
|
+
kid: getDidUriFromJwk(pubK),
|
|
1091
|
+
keyId: keyPairDb._id,
|
|
1092
|
+
},
|
|
1093
|
+
],
|
|
1094
|
+
});
|
|
1095
|
+
validBearer = await generateDocJwt(
|
|
1096
|
+
{ scope: 'jwt test key:create', sub: account._id.toString() },
|
|
1097
|
+
privk
|
|
1098
|
+
);
|
|
1099
|
+
const response = await fastify.injectJson({
|
|
1100
|
+
method: 'POST',
|
|
1101
|
+
url: `${api}/sign`,
|
|
1102
|
+
payload: {
|
|
1103
|
+
header: {},
|
|
1104
|
+
payload: {
|
|
1105
|
+
abc: 'abv',
|
|
1106
|
+
},
|
|
1107
|
+
options: {
|
|
1108
|
+
kid: didJwk,
|
|
1109
|
+
},
|
|
1110
|
+
},
|
|
1111
|
+
headers: {
|
|
1112
|
+
Authorization: `Bearer ${validBearer}`,
|
|
1113
|
+
},
|
|
1114
|
+
});
|
|
1115
|
+
expect(response.statusCode).toEqual(200);
|
|
1116
|
+
expect(response.json).toEqual({
|
|
1117
|
+
compactJwt: expect.any(String),
|
|
1118
|
+
});
|
|
1119
|
+
|
|
1120
|
+
const keyPair = await keyPairsRepo.findOne({
|
|
1121
|
+
filter: {
|
|
1122
|
+
_id: keyPairDb._id,
|
|
1123
|
+
},
|
|
1124
|
+
});
|
|
1125
|
+
const decryptedPrivateKey = await testClient
|
|
1126
|
+
.decrypt({
|
|
1127
|
+
CiphertextBlob: Buffer.from(keyPair.encryptedPrivateKey.buffer),
|
|
1128
|
+
})
|
|
1129
|
+
.promise();
|
|
1130
|
+
const { payload } = await jwtVerify(
|
|
1131
|
+
response.json.compactJwt,
|
|
1132
|
+
jwkFromStringified(decryptedPrivateKey.Plaintext.toString())
|
|
1133
|
+
);
|
|
1134
|
+
expect(payload.abc).toEqual('abv');
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
it('Onboarding new account should successfully sign a jwt with P-256 curv', async () => {
|
|
1138
|
+
const deviceId = nanoid();
|
|
1139
|
+
await persistDevices({ deviceId });
|
|
1140
|
+
const accountResponse = await fastify.injectJson({
|
|
1141
|
+
method: 'POST',
|
|
1142
|
+
url: '/api/v0.6/accounts',
|
|
1143
|
+
payload: {
|
|
1144
|
+
account: {
|
|
1145
|
+
accountType: 'temp',
|
|
1146
|
+
device: {
|
|
1147
|
+
deviceId,
|
|
1148
|
+
deviceType: 'phone',
|
|
1149
|
+
deviceOS: 'ios',
|
|
1150
|
+
},
|
|
1151
|
+
},
|
|
1152
|
+
},
|
|
1153
|
+
});
|
|
1154
|
+
expect(accountResponse.statusCode).toEqual(200);
|
|
1155
|
+
|
|
1156
|
+
const didKeyResponse = await fastify.injectJson({
|
|
1157
|
+
method: 'POST',
|
|
1158
|
+
url: '/api/v0.6/create_did_key',
|
|
1159
|
+
payload: {
|
|
1160
|
+
crv: 'P-256',
|
|
1161
|
+
},
|
|
1162
|
+
headers: {
|
|
1163
|
+
Authorization: `Bearer ${accountResponse.json.access_token}`,
|
|
1164
|
+
},
|
|
1165
|
+
});
|
|
1166
|
+
expect(didKeyResponse.statusCode).toEqual(200);
|
|
1167
|
+
|
|
1168
|
+
const refreshResponse = await fastify.injectJson({
|
|
1169
|
+
method: 'POST',
|
|
1170
|
+
url: '/api/v0.6/oauth/token',
|
|
1171
|
+
payload: {
|
|
1172
|
+
grant_type: 'refresh_token',
|
|
1173
|
+
client_id: 'abc',
|
|
1174
|
+
scope: 'jwt',
|
|
1175
|
+
audience: 'walletapi.velocitycareerlabs.io',
|
|
1176
|
+
refresh_token: accountResponse.json.refresh_token,
|
|
1177
|
+
},
|
|
1178
|
+
});
|
|
1179
|
+
expect(refreshResponse.statusCode).toEqual(200);
|
|
1180
|
+
|
|
1181
|
+
const payload = {
|
|
1182
|
+
vp: {
|
|
1183
|
+
presentation_submission: {
|
|
1184
|
+
descriptor_map: [],
|
|
1185
|
+
definition_id:
|
|
1186
|
+
'65d4f928ffbbda0ea32d159c.634e5620fe258ef4580d3c63',
|
|
1187
|
+
id: 'B424D9E8-4773-49EC-8B66-1D22EACBE848',
|
|
1188
|
+
},
|
|
1189
|
+
},
|
|
1190
|
+
};
|
|
1191
|
+
const response = await fastify.injectJson({
|
|
1192
|
+
method: 'POST',
|
|
1193
|
+
url: `${api}/sign`,
|
|
1194
|
+
payload: {
|
|
1195
|
+
header: {
|
|
1196
|
+
jwk: didKeyResponse.json.publicJwk,
|
|
1197
|
+
kid: didKeyResponse.json.kid,
|
|
1198
|
+
},
|
|
1199
|
+
payload,
|
|
1200
|
+
options: {
|
|
1201
|
+
keyId: didKeyResponse.json.keyId,
|
|
1202
|
+
},
|
|
1203
|
+
},
|
|
1204
|
+
headers: {
|
|
1205
|
+
Authorization: `Bearer ${refreshResponse.json.access_token}`,
|
|
1206
|
+
},
|
|
1207
|
+
});
|
|
1208
|
+
expect(response.statusCode).toEqual(200);
|
|
1209
|
+
expect(response.json).toEqual({
|
|
1210
|
+
compactJwt: expect.any(String),
|
|
1211
|
+
});
|
|
1212
|
+
const presentation = await verifyPresentationJwt(
|
|
1213
|
+
response.json.compactJwt
|
|
1214
|
+
);
|
|
1215
|
+
expect(presentation.presentation_submission).toEqual(
|
|
1216
|
+
payload.vp.presentation_submission
|
|
1217
|
+
);
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
it('Onboarding new account should successfully sign a jwt with secp256k1 curv', async () => {
|
|
1221
|
+
const deviceId = nanoid();
|
|
1222
|
+
await persistDevices({ deviceId });
|
|
1223
|
+
const accountResponse = await fastify.injectJson({
|
|
1224
|
+
method: 'POST',
|
|
1225
|
+
url: '/api/v0.6/accounts',
|
|
1226
|
+
payload: {
|
|
1227
|
+
account: {
|
|
1228
|
+
accountType: 'temp',
|
|
1229
|
+
device: {
|
|
1230
|
+
deviceId,
|
|
1231
|
+
deviceType: 'phone',
|
|
1232
|
+
deviceOS: 'ios',
|
|
1233
|
+
},
|
|
1234
|
+
},
|
|
1235
|
+
},
|
|
1236
|
+
});
|
|
1237
|
+
expect(accountResponse.statusCode).toEqual(200);
|
|
1238
|
+
|
|
1239
|
+
const didKeyResponse = await fastify.injectJson({
|
|
1240
|
+
method: 'POST',
|
|
1241
|
+
url: '/api/v0.6/create_did_key',
|
|
1242
|
+
payload: {
|
|
1243
|
+
crv: 'secp256k1',
|
|
1244
|
+
},
|
|
1245
|
+
headers: {
|
|
1246
|
+
Authorization: `Bearer ${accountResponse.json.access_token}`,
|
|
1247
|
+
},
|
|
1248
|
+
});
|
|
1249
|
+
expect(didKeyResponse.statusCode).toEqual(200);
|
|
1250
|
+
|
|
1251
|
+
const refreshResponse = await fastify.injectJson({
|
|
1252
|
+
method: 'POST',
|
|
1253
|
+
url: '/api/v0.6/oauth/token',
|
|
1254
|
+
payload: {
|
|
1255
|
+
grant_type: 'refresh_token',
|
|
1256
|
+
client_id: 'abc',
|
|
1257
|
+
scope: 'jwt',
|
|
1258
|
+
audience: 'walletapi.velocitycareerlabs.io',
|
|
1259
|
+
refresh_token: accountResponse.json.refresh_token,
|
|
1260
|
+
},
|
|
1261
|
+
});
|
|
1262
|
+
expect(refreshResponse.statusCode).toEqual(200);
|
|
1263
|
+
|
|
1264
|
+
const payload = {
|
|
1265
|
+
vp: {
|
|
1266
|
+
presentation_submission: {
|
|
1267
|
+
descriptor_map: [],
|
|
1268
|
+
definition_id:
|
|
1269
|
+
'65d4f928ffbbda0ea32d159c.634e5620fe258ef4580d3c63',
|
|
1270
|
+
id: 'B424D9E8-4773-49EC-8B66-1D22EACBE848',
|
|
1271
|
+
},
|
|
1272
|
+
},
|
|
1273
|
+
};
|
|
1274
|
+
const account = await persistAccounts({
|
|
1275
|
+
didKeyMetadatum: [
|
|
1276
|
+
{
|
|
1277
|
+
kid: didKeyResponse.json.kid,
|
|
1278
|
+
keyId: didKeyResponse.json.keyId,
|
|
1279
|
+
},
|
|
1280
|
+
],
|
|
1281
|
+
});
|
|
1282
|
+
validBearer = await generateDocJwt(
|
|
1283
|
+
{ scope: 'jwt test key:create', sub: account._id.toString() },
|
|
1284
|
+
privk
|
|
1285
|
+
);
|
|
1286
|
+
const response = await fastify.injectJson({
|
|
1287
|
+
method: 'POST',
|
|
1288
|
+
url: `${api}/sign`,
|
|
1289
|
+
payload: {
|
|
1290
|
+
header: {
|
|
1291
|
+
jwk: didKeyResponse.json.publicJwk,
|
|
1292
|
+
kid: didKeyResponse.json.kid,
|
|
1293
|
+
},
|
|
1294
|
+
payload,
|
|
1295
|
+
options: {
|
|
1296
|
+
keyId: didKeyResponse.json.keyId,
|
|
1297
|
+
},
|
|
1298
|
+
},
|
|
1299
|
+
headers: {
|
|
1300
|
+
Authorization: `Bearer ${refreshResponse.json.access_token}`,
|
|
1301
|
+
},
|
|
1302
|
+
});
|
|
1303
|
+
expect(response.statusCode).toEqual(200);
|
|
1304
|
+
expect(response.json).toEqual({
|
|
1305
|
+
compactJwt: expect.any(String),
|
|
1306
|
+
});
|
|
1307
|
+
const presentation = await verifyPresentationJwt(
|
|
1308
|
+
response.json.compactJwt
|
|
1309
|
+
);
|
|
1310
|
+
expect(presentation.presentation_submission).toEqual(
|
|
1311
|
+
payload.vp.presentation_submission
|
|
1312
|
+
);
|
|
1313
|
+
});
|
|
1314
|
+
});
|
|
1315
|
+
});
|
|
1316
|
+
|
|
1317
|
+
describe('/jwt', () => {
|
|
1318
|
+
describe('JWT Decoding Tests', () => {
|
|
1319
|
+
it('should decode a jwt if correct access token is provided', async () => {
|
|
1320
|
+
const obj = {
|
|
1321
|
+
field1: 'value1',
|
|
1322
|
+
};
|
|
1323
|
+
const jwtString = await generateDocJwt(obj, privateKey);
|
|
1324
|
+
const response = await fastify.injectJson({
|
|
1325
|
+
method: 'POST',
|
|
1326
|
+
url: `${buildUrl()}/decode`,
|
|
1327
|
+
payload: {
|
|
1328
|
+
jwt: jwtString,
|
|
1329
|
+
},
|
|
1330
|
+
headers: {
|
|
1331
|
+
authorization: `Bearer ${validBearer}`,
|
|
1332
|
+
},
|
|
1333
|
+
});
|
|
1334
|
+
|
|
1335
|
+
expect(response.statusCode).toEqual(200);
|
|
1336
|
+
expect(response.json).toEqual({
|
|
1337
|
+
payload: {
|
|
1338
|
+
...obj,
|
|
1339
|
+
iat: expect.any(Number),
|
|
1340
|
+
nbf: expect.any(Number),
|
|
1341
|
+
},
|
|
1342
|
+
...expectedHeader,
|
|
1343
|
+
});
|
|
1344
|
+
});
|
|
1345
|
+
|
|
1346
|
+
it('should return 401 when no access token is provided', async () => {
|
|
1347
|
+
const response = await fastify.injectJson({
|
|
1348
|
+
method: 'POST',
|
|
1349
|
+
url: `${buildUrl()}/decode`,
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1352
|
+
missingAccessTokenExpectation(response);
|
|
1353
|
+
});
|
|
1354
|
+
|
|
1355
|
+
it('should return 401 when incorrect access token is provided', async () => {
|
|
1356
|
+
const response = await fastify.injectJson({
|
|
1357
|
+
method: 'POST',
|
|
1358
|
+
url: `${buildUrl()}/decode`,
|
|
1359
|
+
headers: {
|
|
1360
|
+
authorization: 'Bearer incorrect_access_token',
|
|
1361
|
+
},
|
|
1362
|
+
});
|
|
1363
|
+
|
|
1364
|
+
incorrectAccessTokenExpectation(response);
|
|
1365
|
+
});
|
|
1366
|
+
|
|
1367
|
+
it('should return 403 when incorrect scope is provided', async () => {
|
|
1368
|
+
const response = await fastify.injectJson({
|
|
1369
|
+
method: 'POST',
|
|
1370
|
+
url: `${buildUrl()}/decode`,
|
|
1371
|
+
headers: {
|
|
1372
|
+
authorization: `Bearer ${withoutScopeBearer}`,
|
|
1373
|
+
},
|
|
1374
|
+
});
|
|
1375
|
+
|
|
1376
|
+
incorrectScopeExpectation(response);
|
|
1377
|
+
});
|
|
1378
|
+
});
|
|
1379
|
+
|
|
1380
|
+
describe('JWT Signing Tests', () => {
|
|
1381
|
+
it('should sign a jwt with options if correct access token is provided', async () => {
|
|
1382
|
+
const issuerValue = nanoid();
|
|
1383
|
+
const audienceValue = nanoid();
|
|
1384
|
+
const subjectValue = nanoid();
|
|
1385
|
+
const durationObj = { days: 7 };
|
|
1386
|
+
const expiresInValue = formatISODuration(durationObj);
|
|
1387
|
+
const obj = {
|
|
1388
|
+
field1: 'value1',
|
|
1389
|
+
};
|
|
1390
|
+
const options = {
|
|
1391
|
+
issuer: issuerValue,
|
|
1392
|
+
audience: audienceValue,
|
|
1393
|
+
subject: subjectValue,
|
|
1394
|
+
expiresIn: expiresInValue,
|
|
1395
|
+
};
|
|
1396
|
+
|
|
1397
|
+
const response = await fastify.injectJson({
|
|
1398
|
+
method: 'POST',
|
|
1399
|
+
url: `${buildUrl()}/sign`,
|
|
1400
|
+
payload: {
|
|
1401
|
+
payload: obj,
|
|
1402
|
+
options,
|
|
1403
|
+
},
|
|
1404
|
+
headers: {
|
|
1405
|
+
authorization: `Bearer ${validBearer}`,
|
|
1406
|
+
},
|
|
1407
|
+
});
|
|
1408
|
+
expect(response.statusCode).toEqual(200);
|
|
1409
|
+
const decodedJwt = jwtDecode(response.json.jwt);
|
|
1410
|
+
expect(decodedJwt).toEqual({
|
|
1411
|
+
payload: {
|
|
1412
|
+
...obj,
|
|
1413
|
+
sub: subjectValue,
|
|
1414
|
+
aud: audienceValue,
|
|
1415
|
+
iss: issuerValue,
|
|
1416
|
+
jti: expect.any(String),
|
|
1417
|
+
iat: expect.any(Number),
|
|
1418
|
+
nbf: expect.any(Number),
|
|
1419
|
+
exp: expect.any(Number),
|
|
1420
|
+
},
|
|
1421
|
+
...expectedHeader,
|
|
1422
|
+
});
|
|
1423
|
+
});
|
|
1424
|
+
|
|
1425
|
+
it('should return 401 when no access token is provided', async () => {
|
|
1426
|
+
const response = await fastify.injectJson({
|
|
1427
|
+
method: 'POST',
|
|
1428
|
+
url: `${buildUrl()}/sign`,
|
|
1429
|
+
});
|
|
1430
|
+
|
|
1431
|
+
missingAccessTokenExpectation(response);
|
|
1432
|
+
});
|
|
1433
|
+
|
|
1434
|
+
it('should return 401 when incorrect access token is provided', async () => {
|
|
1435
|
+
const response = await fastify.injectJson({
|
|
1436
|
+
method: 'POST',
|
|
1437
|
+
url: `${buildUrl()}/sign`,
|
|
1438
|
+
headers: {
|
|
1439
|
+
authorization: 'Bearer incorrect_access_token',
|
|
1440
|
+
},
|
|
1441
|
+
});
|
|
1442
|
+
|
|
1443
|
+
incorrectAccessTokenExpectation(response);
|
|
1444
|
+
});
|
|
1445
|
+
|
|
1446
|
+
it('should return 403 when incorrect scope is provided', async () => {
|
|
1447
|
+
const response = await fastify.injectJson({
|
|
1448
|
+
method: 'POST',
|
|
1449
|
+
url: `${buildUrl()}/sign`,
|
|
1450
|
+
headers: {
|
|
1451
|
+
authorization: `Bearer ${withoutScopeBearer}`,
|
|
1452
|
+
},
|
|
1453
|
+
});
|
|
1454
|
+
|
|
1455
|
+
incorrectScopeExpectation(response);
|
|
1456
|
+
});
|
|
1457
|
+
});
|
|
1458
|
+
|
|
1459
|
+
describe('JWT Verification Tests', () => {
|
|
1460
|
+
it('should verify a jwt using an explicit public key if correct access token is provided', async () => {
|
|
1461
|
+
const obj = {
|
|
1462
|
+
field1: 'value1',
|
|
1463
|
+
};
|
|
1464
|
+
const jwt = await jwtSign(obj, privateJwk);
|
|
1465
|
+
|
|
1466
|
+
const response = await fastify.injectJson({
|
|
1467
|
+
method: 'POST',
|
|
1468
|
+
url: `${buildUrl()}/verify`,
|
|
1469
|
+
payload: {
|
|
1470
|
+
jwt,
|
|
1471
|
+
publicKey: publicJwk,
|
|
1472
|
+
},
|
|
1473
|
+
headers: {
|
|
1474
|
+
authorization: `Bearer ${validBearer}`,
|
|
1475
|
+
},
|
|
1476
|
+
});
|
|
1477
|
+
|
|
1478
|
+
expect(response.statusCode).toEqual(200);
|
|
1479
|
+
expect(response.json).toEqual({
|
|
1480
|
+
verified: true,
|
|
1481
|
+
});
|
|
1482
|
+
});
|
|
1483
|
+
|
|
1484
|
+
it('should return 401 when no access token is provided', async () => {
|
|
1485
|
+
const response = await fastify.injectJson({
|
|
1486
|
+
method: 'POST',
|
|
1487
|
+
url: `${buildUrl()}/verify`,
|
|
1488
|
+
});
|
|
1489
|
+
|
|
1490
|
+
missingAccessTokenExpectation(response);
|
|
1491
|
+
});
|
|
1492
|
+
|
|
1493
|
+
it('should return 401 when incorrect access token is provided', async () => {
|
|
1494
|
+
const response = await fastify.injectJson({
|
|
1495
|
+
method: 'POST',
|
|
1496
|
+
url: `${buildUrl()}/verify`,
|
|
1497
|
+
headers: {
|
|
1498
|
+
authorization: 'Bearer incorrect_access_token',
|
|
1499
|
+
},
|
|
1500
|
+
});
|
|
1501
|
+
|
|
1502
|
+
incorrectAccessTokenExpectation(response);
|
|
1503
|
+
});
|
|
1504
|
+
|
|
1505
|
+
it('should return 403 when incorrect scope is provided', async () => {
|
|
1506
|
+
const response = await fastify.injectJson({
|
|
1507
|
+
method: 'POST',
|
|
1508
|
+
url: `${buildUrl()}/verify`,
|
|
1509
|
+
headers: {
|
|
1510
|
+
authorization: `Bearer ${withoutScopeBearer}`,
|
|
1511
|
+
},
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
incorrectScopeExpectation(response);
|
|
1515
|
+
});
|
|
1516
|
+
});
|
|
1517
|
+
});
|
|
1518
|
+
});
|
|
1519
|
+
});
|