Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upserver: require signed requests #4
Conversation
|
travis is error'ing at /home/travis/build/brave/sync/test/server/users.js:32 |
| return response.status(400).end('Unable to verify the signed request. Please check the signing private key matches the pubkey.') | ||
| } | ||
|
|
||
| const result = serializer.serializer.byteArrayToString(verifiedBytes) |
This comment has been minimized.
This comment has been minimized.
diracdeltas
Dec 6, 2016
Member
this needs to handle the case where serializer has not finished initializing; it should probably wait 1s and try again
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
diracdeltas
Dec 6, 2016
Member
not easily; i guess you could return a 503 and tell the client to retry the request after some time
| if (!body || body.length === 0) { | ||
| return response.status(400).end('Signed request body is required.') | ||
| } | ||
| const publicKey = Buffer.from(userId, 'base64') |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
ayumi
Dec 6, 2016
Author
Contributor
i think it's done automagically – if the corresponding test passes consistently it's probably okay?
This comment has been minimized.
This comment has been minimized.
diracdeltas
Dec 6, 2016
Member
https://nodejs.org/api/buffer.html#buffer_class_method_buffer_from_string_encoding indicates its prolly fine
| return response.status(400).end('Signed request body is required.') | ||
| } | ||
| const publicKey = Buffer.from(userId, 'base64') | ||
| const verifiedBytes = crypto.verify(Buffer.from(body), publicKey) |
This comment has been minimized.
This comment has been minimized.
| function signedTimestamp (secretKey, timestamp) { | ||
| if (!timestamp) { timestamp = Math.floor(Date.now() / 1000) } | ||
| const message = timestamp.toString() | ||
| return crypto.sign(serializer.serializer.stringToByteArray(message), secretKey) |
This comment has been minimized.
This comment has been minimized.
diracdeltas
Dec 6, 2016
Member
this needs to ensure that serializer is initialized; i handled this by putting the tests in the serializer.init callback in test/serializer.js
| function signedTimestamp (secretKey, timestamp) { | ||
| if (!timestamp) { timestamp = Math.floor(Date.now() / 1000) } | ||
| const message = timestamp.toString() | ||
| return crypto.sign(serializer.serializer.stringToByteArray(message), secretKey) |
This comment has been minimized.
This comment has been minimized.
2e529ab
to
76084e2
|
thanks, have updated serializer and tests to wait until it's been initialized. |
|
|
||
| module.exports.serializer = null | ||
|
|
||
| module.exports.initSerializer = function (apiFile/* : string */) { |
This comment has been minimized.
This comment has been minimized.
ayumi
Dec 6, 2016
Author
Contributor
Updated this to return a Promise
maybe init() above should just do this?
i made initSerializer() so a whole app might share a single serializer instance, since it's kind of #effort to reinit the serializer each time we need it.
This comment has been minimized.
This comment has been minimized.
diracdeltas
Dec 6, 2016
Member
yeah seems like this should just be a modification to init instead of its own method
| return response.status(400).end('Unable to verify the signed request. Please check the signing private key matches the pubkey.') | ||
| } | ||
|
|
||
| const result = serializer.serializer.byteArrayToString(verifiedBytes) |
This comment has been minimized.
This comment has been minimized.
| } else { | ||
| t.fail(`${t.name} (${response.statusCode}) (${body})`) | ||
| } | ||
| // TODO: Use protobuf? |
This comment has been minimized.
This comment has been minimized.
| const serializer = require('../../../lib/serializer.js') | ||
| const Express = require('express') | ||
|
|
||
| test('users router', (t) => { |
This comment has been minimized.
This comment has been minimized.
diracdeltas
Dec 6, 2016
Member
for async tests, i think it is best practice to use t.plan so that the test doesn't abort early if the callback with t.end gets run before some of the other callbacks
| @@ -3,90 +3,122 @@ const awsSdk = require('aws-sdk') | |||
| const config = require('config') | |||
| const crypto = require('../../lib/crypto') | |||
| const request = require('request') | |||
| const supertest = require('supertest') | |||
| const serializer = require('../../lib/serializer.js') | |||
| const usersRouter = require('../../server/users.js') | |||
| const util = require('../../server/lib/util.js') | |||
| const Express = require('express') | |||
|
|
|||
| test('users router', (t) => { | |||
This comment has been minimized.
This comment has been minimized.
|
sweet tests |
|
have updated with:
@diracdeltas can u pls take another look |
| /** | ||
| * Deserializes client sync credentials for accessing AWS and S3. | ||
| * @param {Uint8Array} bytes | ||
| * @returns {{aws: <Uint8Array>, s3Post: <Uint8Array>}} |
This comment has been minimized.
This comment has been minimized.
diracdeltas
Dec 7, 2016
Member
surprised that this deserializes to Uint8Array in the fields instead of the credentials object
This comment has been minimized.
This comment has been minimized.
| Serializer.prototype.credentialsToByteArray = function (credentials) { | ||
| return this.api.Credentials.encode({ | ||
| aws: this.api.Credentials.lookup('Aws').create(credentials.aws), | ||
| s3Post: this.api.Credentials.lookup('S3Post').create(credentials.s3Post) |
This comment has been minimized.
This comment has been minimized.
diracdeltas
Dec 7, 2016
•
Member
does this.api.Credentials.encode(credentials) work here? i think that would avoid the fields being Uint8Arrays instead of the original object types
This comment has been minimized.
This comment has been minimized.
ayumi
Dec 7, 2016
Author
Contributor
see: typo comment above regarding return of decode not being UInt8Array
I think encode() for a type with nested types only works when the nested types are legit instances
{
aws: {Credentials.Aws},
s3Post: {Credentials.S3Post}
}
This comment has been minimized.
This comment has been minimized.
diracdeltas
Dec 7, 2016
Member
so the fields are the actual types instead of Uint8Arrays with this the way it is now? would be good to have a test to prove that
This comment has been minimized.
This comment has been minimized.
| @@ -5,16 +5,22 @@ const pb = require('protobufjs') | |||
|
|
|||
| module.exports.init = function (apiFile/* : string */) { | |||
| return pb.load(apiFile || './lib/api.proto').then((r) => { | |||
| return new Serializer({ | |||
| if (this.serializer) { return this.serializer } | |||
This comment has been minimized.
This comment has been minimized.
diracdeltas
Dec 7, 2016
Member
i'm not sure this line helps actually. https://nodejs.org/api/modules.html#modules_caching suggests that every module import gets the same object, so calling 'init' won't help future imports.
This comment has been minimized.
This comment has been minimized.
ayumi
Dec 7, 2016
Author
Contributor
yes, but we've been calling init() which always instantiates new Serializers.
with this return, it caches a single Serializer instance onto the shared cached serializer module and returns it next time we try to init()
This comment has been minimized.
This comment has been minimized.
diracdeltas
Dec 7, 2016
Member
i see; i didn't intend multiple calls of init in the same module to be used outside of tests. the intended pattern is module calls init, saves the serializer in its own scope, calls the saved serializer in the future.
not a big deal either way
ayumi commentedDec 6, 2016
request-verifierwhich verifies signed timestamp in request bodies.request-verifierto verify requests toPOST /{userId}/credentials.auditors: @diracdeltas