Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

epic: Server rewrite #10

Merged
merged 61 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
197654a
Replace hardcoded values with ENV variables and starts implementing l…
Danziger Jul 5, 2024
2f181aa
Implement a mocked/local version of KMS client to easily run the serv…
Danziger Jul 8, 2024
31c48d0
Remove old TODO.
Danziger Jul 8, 2024
97fab7e
Implement JWT validation using Auth0's middleware and JWKS client and…
Danziger Jul 11, 2024
9a15deb
Remove TODOs from README.
Danziger Jul 11, 2024
df9d57f
Add idToken in all calls to logRequestSuccess.
Danziger Jul 11, 2024
ec02249
Remove unused import.
Danziger Jul 11, 2024
c7de33a
Reformat code.
Danziger Jul 11, 2024
066f4d9
Remove audience from token verification.
Danziger Jul 11, 2024
65c5d7c
Improve server error handling.
Danziger Jul 12, 2024
12cf5dc
Re-implement asyncHandler with working TS types.
Danziger Jul 12, 2024
0cf6211
Finish Config wrapper for ENV variables.
Danziger Jul 12, 2024
ae06ce3
Remove some old comments / unused code.
Danziger Jul 12, 2024
6e009a4
Remove unused dependencies.
Danziger Jul 12, 2024
929e7c1
Add Auth0's actions.
Danziger Jul 15, 2024
be5e9e7
Add missing fields in IdTokenWithData.
Danziger Jul 15, 2024
99132a8
Add missing fields in IdTokenWithData.
Danziger Jul 15, 2024
a699825
Improve custom error implementation.
Danziger Jul 15, 2024
d0ff5ca
Update dev script to use Bun's watch mode.
Danziger Jul 15, 2024
0d43b72
Remove create-bundle-and-sign endpoint as it was not used anyway.
Danziger Jul 15, 2024
816b8bf
Add some changes to the actions to work with query and body data.
Danziger Jul 16, 2024
1e71199
Improve b64 encoding type and parsing.
Danziger Jul 18, 2024
d45e859
Update handlers to be compatible with the old request and response fo…
Danziger Sep 2, 2024
0d7587a
Update server code to import keys (instead of creating them).
Danziger Sep 2, 2024
35b352d
Move all KMS path bits to CONFIG and conditionally create import jobs…
Danziger Sep 3, 2024
bfa3092
Mock KMS functions needed to import keys.
Danziger Sep 4, 2024
3e9dd5a
Reorganize createUser and import key functions and handlers and creat…
Danziger Sep 4, 2024
393f224
Improve normalization / serialization of binary data.
Danziger Sep 4, 2024
f77978c
Add toLegacyBufferObject() util function.
Danziger Sep 5, 2024
32e7afe
Update debugging code.
Danziger Sep 5, 2024
213867f
Update config.utils.ts KMS credentials validation.
Danziger Sep 5, 2024
5152f9f
Merge pull request #11 from Othent/feature/import-keys-poc
Danziger Sep 5, 2024
1938d8e
Add TODO.
Danziger Sep 5, 2024
7684152
Document old vs new server and SDK interfaces.
Danziger Sep 5, 2024
6c2ce0c
Add Zod schemas for createUser(), encrypt(), decryt() and sign().
Danziger Sep 6, 2024
2c78380
Update Zod schemas.
Danziger Sep 6, 2024
803c982
Fix KMS client mock.
Danziger Sep 6, 2024
957187e
Fix KMS mock.
Danziger Sep 9, 2024
aa01c2a
Update KMS mock return value types.
Danziger Sep 9, 2024
3a42bcd
Add unit tests for encrypt().
Danziger Sep 10, 2024
eca56b6
Add tests for decrypt().
Danziger Sep 11, 2024
267a8ee
Finishes all tests and validation (verifySignature to be fixed).
Danziger Sep 11, 2024
a4d43dd
Fix sign() tests.
Danziger Sep 11, 2024
df8ff86
Fix typo.
Danziger Sep 11, 2024
0e5db6a
Update encrypt(), decryp() and sign() to return/send B64String.
Danziger Sep 11, 2024
1d1f8c1
createUser() returns the IdToken's data.
Danziger Sep 12, 2024
49a447f
Separate auth0.types from auth0.utils.
Danziger Sep 12, 2024
4f8086d
Fix imports.
Danziger Sep 12, 2024
45802f9
Remove exact() from validators.
Danziger Sep 12, 2024
5db2dd2
Remove console.log()s.
Danziger Sep 12, 2024
6635744
Fix test NPM script.
Danziger Sep 12, 2024
eaa7c7f
Fix test setup.
Danziger Sep 12, 2024
349857a
Update src/lib/utils/kms/google-kms.utils.ts
Danziger Sep 16, 2024
aeab154
Update .env.example.
Danziger Sep 16, 2024
222d8fe
Update Auth0 M2M API URL.
Danziger Sep 16, 2024
f01a9f2
Add developer error message about time synching with KMS.
Danziger Sep 16, 2024
1b6b652
Update GitHub actions.
Danziger Sep 23, 2024
a0b5abe
Update all commands in test-server.yml.
Danziger Sep 23, 2024
2a95c7a
Merge pull request #16 from Othent/feature/update-github-actions
Danziger Sep 23, 2024
faf6819
Update engines.
Danziger Sep 23, 2024
96fe5ae
Update engines.
Danziger Sep 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.{sh,bash}]
end_of_line = lf
16 changes: 12 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
auth0ClientId=''
auth0CustomDomain='auth.othent.io'

auth0ClientSecret=''
auth0ClientDomain='<MACHINE_TO_MACHNE_APP_DOMAIN>'

kmsProjectId=''
auth0ClientId='<MACHINE_TO_MACHNE_APP_ID>'

auth0ClientSecret='<MACHINE_TO_MACHINE_APP_SECRET>'

kmsProjectId='othent-kms-local'

kmsProjectLocation='global'

googleCredentials=`{}`

signKeyVersion=''

PORT=''
encryptDecryptKeyVersion=''

PORT='3010'

mongoDBUsername=''

Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/test-server.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ jobs:
run: npm install
- name: 🧪 Run tests
env:
auth0CustomDomain: ${{ secrets.AUTH0CUSTOMDOMAIN }}
auth0ClientDomain: ${{ secrets.AUTH0CLIENTDOMAIN }}
auth0ClientId: ${{ secrets.AUTH0CLIENTID }}
auth0ClientSecret: ${{ secrets.AUTH0CLIENTSECRET }}
kmsProjectId: ${{ secrets.KMSPROJECTID }}
kmsProjectLocation: ${{ secrets.KMSPROJECTLOCATION }}
googleCredentials: ${{ secrets.GOOGLECREDENTIALS }}
signKeyVersion: ${{ secrets.SIGNKEYVERSION }}
encryptDecryptKeyVersion: ${{ secrets.ENCRYPTDECRYPTKEYVERSION }}
PORT: ${{ secrets.PORT }}
mongoDBUsername: ${{ secrets.mongoDBUsername }}
mongoDBPassword: ${{ secrets.mongoDBPassword }}
Expand Down
137 changes: 135 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,136 @@
# KMS-server-new
# KMS Server (New)

Server for KMS interactions.
Node.js/Express.js server to interact with Auth0 and Google KMS.

<br />

## Running It Locally:

First, you need to update `.env` with a valid `auth0CustomDomain`, `auth0ClientDomain`, `auth0ClientId` and `auth0ClientSecret` values pointing to an Auth0
application with the following params:

- Type: `Machine to Machine`.
- Permissions / Scopes: `read:client_credentials` and `update:users` (this cannot be change after creating the Application).
- Credentials: `Client Secret (Post)`.
- Grant Types: `Client Credentials`.

Also, the tenant both this Machine to Machine application and the Single Page application used the KMS SDK (`KeyManagementService`) you are using should have
at least the `actions/add-user-metadata.ts` action configured in its Login flow.

Then, simply run:

```
pnpm install
pnpm start
```

You don't need to configure the remaining 3 services:

- Google KMS will be replaced by a local mocked implementation (which uses the same keys for all users).
- MongoDB database won't be used (this logic is skipped).
- Slack integration won't be used (this logic is skipped).

<br />

## Running It In Production:

Remember you need to set the different variables in `.env` or your Cloud provider's secrets to enable the following services:

- Auth0's Machine to Machine and Single Page Applications applications, with the right actions configured in their tenant's Login flow
- Google KMS.
- MongoDB database.
- Slack integration.

## Testing And Backwards Compatibility:

As there's only one production / live version of the server project at a time, tests need to make sure that the current
server can take request and send responses that work with both the old SDK and the new SDK.

Below we can see a breakdown of the main server functions and the data types the different SDK versions would send to
them and expect as response.

Also note that, right now the request data types mentioned below are the ones included as `data` in Auth0's ID token.
- The response data types mentioned below are sent directly, mea

### Old SDK (v1)

To verify that the server is backwards-compatible with the old SDK you should run `sdk-v1-compatibility.e2e.ts`.
Alternatively, you can also run the playground locally with an old `v1` SDK plus an adapter, but that won't be as
exhaustive as the test and some of the param types mentioned below won't be tested:

- `createUser`

- The old server expects no data in `createUser`.

- The old server replies with `true` if the user was successfully created.

- `encrypt`

- The old SDK (according to the docs) takes `Uint8Array | string`, but in practice the old server only accepts
`LegacyBufferObject | string`, as the `Uint8Array` would be serialized using `JSON.stringify()` and deserialized
using `Buffer.from()`, which would not accept the resulting `LegacyBufferRecord`.

See:

- https://github.com/Othent/KeyManagementService/pull/18/files#diff-3196ebbd6ec8d2b54d0da4680941db6b502b1c82fa7366c72746e4f1a41a4a96
- https://github.com/Othent/KMS-server-new/pull/10#discussion_r1682666284

- The old server would reply with a `LegacyBufferObject` and the old SDK would return that as-is.

See https://github.com/Othent/KeyManagementService/pull/18/files#r1686770313

- `decrypt`

- The old SDK (according to the examples) takes `LegacyBufferObject` (as returned from `encrypt`), but as the old
server deserializes that using `Buffer.form()`, a `string` would also be valid if encoded properly.

See:

- https://github.com/Othent/KMS-server-new/pull/10/files#diff-547cb059a26afae3681f1c161d74dedf9c6872fd8ac50a1fbe7dcce6eeb544ec
- https://github.com/Othent/KeyManagementService/pull/18/files#r1686770313

- The old server would reply with a plain `string`.

See https://github.com/Othent/KMS-server-new/pull/10/files#r1745868667

- `sign`

- The old SKD would only accept binary data that, after being serialized with `JSON.stringify()` would result in a
`LegacyBufferRecord`, which would then be deserialized by the old server with `new Uint8Array(Object.values(data))`

See https://github.com/Othent/KMS-server-new/pull/10#discussion_r1682671389

- The old server would reply with a `LegacyBufferObject` and the old SDK would return that as-is.

See https://github.com/Othent/KeyManagementService/pull/18/files#r1686770313


### New SDK (v2.0.0)

Version `2.0.0` of `@othent/kms` improves and normalizes the data types accepted and returned by the different
functions, but doesn't change the shape in which that data is sent to the server, so that's still `LegacyBufferRecord`
for `sign()` and `LegacyBufferObject` for the other functions. That means that this new version of the SDK works with
the old backend.

The only breaking change is that `decrypt` would not accept `BufferObject` / `LegacyBufferObject` automatically, and
developers upgrading to `2.0.0` would have to convert those manually.

See: https://github.com/Othent/KeyManagementService/pull/18/files#r1686770313

### New / Upcoming Server & SDK (v2.1.0)

For the server, particularly:

- If `data.keyName` is present, validate the data according to the format / shape required by the old server. The
exception to this is `createUser`, which would not have any `data`.

- If `data.path` is present, validate the data according to the format / shape required by the new server, and unwrap it
(remove that unnecessary `data` (`data.data`) property in the response).

For the SDK, particularly:

- Remove `data.keyName`.
- Add `data.path` and validate this on the server as well.
- Stop using `BufferObject` / `LegacyBufferObject` / `LegacyBufferRecord` and instead serialize/send the data as
`B64string`.
- Also update the parsing of the responses after the server stops sending that unnecessary `data` property.
7 changes: 7 additions & 0 deletions auth0/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@


See:

- https://auth0.com/docs/customize/actions/migrate/migrate-from-rules-to-actions

-
51 changes: 51 additions & 0 deletions auth0/actions/add-user-metadata.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Handler that will be called during the execution of a PostLogin flow.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/
exports.onExecutePostLogin = async (event, api) => {
if (event.authorization) {
// Won't work without an extension. See https://auth0.com/docs/customize/extensions/real-time-webtask-logs
// ONLY USE LOGGING IN DEVELOPMENT!
// console.log(JSON.stringify(event.request, null, ' '));

const transaction_input = event.request.query.transaction_input || event.request.body.transaction_input;

let transactionInput = null;

try {
transactionInput = JSON.parse(transaction_input);

if (!transactionInput) throw new Error("Missing `transaction_input`");
} catch (err) {
console.log(err.name, err.message);

return;
}

if (transactionInput.othentFunction === "KMS") {
if (transactionInput.data) {
api.idToken.setCustomClaim(`data`, transactionInput.data);
}

if (event.user.user_metadata.owner) {
api.idToken.setCustomClaim(`owner`, event.user.user_metadata.owner);
}

if (event.user.user_metadata.walletAddress) {
api.idToken.setCustomClaim(
`walletAddress`,
event.user.user_metadata.walletAddress,
);
}

if (event.user.user_metadata.authSystem) {
api.idToken.setCustomClaim(
`authSystem`,
event.user.user_metadata.authSystem,
);
}
}
}
};
12 changes: 12 additions & 0 deletions auth0/actions/enforce-email-verification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Handler that will be called during the execution of a PostLogin flow.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/
// @ts-ignore
exports.onExecutePostLogin = async (event, api) => {
if (!event.user.email_verified) {
return api.access.deny("Please verify your email before logging in.");
}
};
27 changes: 27 additions & 0 deletions auth0/actions/privatise-jwt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Handler that will be called during the execution of a PostLogin flow.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/
// @ts-ignore
exports.onExecutePostLogin = async (event, api) => {
const { user } = event;

// Won't work without an extension. See https://auth0.com/docs/customize/extensions/real-time-webtask-logs
// ONLY USE LOGGING IN DEVELOPMENT!
// console.log("user =", user);
// console.log("user.private =", user.private);

if (user.private === true) {
api.idToken.setCustomClaim("email", "");
api.idToken.setCustomClaim("email_verified", "");
api.idToken.setCustomClaim("family_name", "");
api.idToken.setCustomClaim("given_name", "");
api.idToken.setCustomClaim("locale", "");
api.idToken.setCustomClaim("nickname", "");
api.idToken.setCustomClaim(`picture`, "https://othent.io/user.png");
}

return;
};
Loading
Loading