-
Notifications
You must be signed in to change notification settings - Fork 0
ServiceContext #19
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
base: master
Are you sure you want to change the base?
ServiceContext #19
Changes from all commits
13f92ec
db46883
e89cd37
1d06814
62f0214
91504ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,202 @@ | ||
| const crypto = require('crypto'); | ||
| const KeyJar = require('../nodeOIDCMsg/src/oicMsg/keystore/KeyJar').KeyJar; | ||
|
|
||
| const ATTRMAP = { | ||
| 'userinfo': { | ||
| 'sign': 'userinfo_signed_response_alg', | ||
| 'alg': 'userinfo_encrypted_response_alg', | ||
| 'enc': 'userinfo_encrypted_response_enc' | ||
| }, | ||
| 'id_token': { | ||
| 'sign': 'id_token_signed_response_alg', | ||
| 'alg': 'id_token_encrypted_response_alg', | ||
| 'enc': 'id_token_encrypted_response_enc' | ||
| }, | ||
| 'request': { | ||
| 'sign': 'request_object_signing_alg', | ||
| 'alg': 'request_object_encryption_alg', | ||
| 'enc': 'request_object_encryption_enc' | ||
| } | ||
| }; | ||
|
|
||
| const DEFAULT_SIGN_ALG = { | ||
| 'userinfo': 'RS256', | ||
| 'request': 'RS384', | ||
| 'id_token': 'ES384', | ||
| }; | ||
|
|
||
| /** | ||
| * This class keeps information that a client needs to be able to talk | ||
| * to a server. Some of this information comes from configuration and some | ||
| * from dynamic provider info discovery or client registration. | ||
| * But information is also picked up during the conversation with a server. | ||
| */ | ||
| class ServiceContext { | ||
| /** | ||
| * @param {KeyJar} keyjar OIDCMsg KeyJar instance that contains the RP signing and encyrpting keys | ||
| * @param {Object<string, string>} config Client configuration | ||
| * @param {Object<string, string>} params Other attributes that might be needed | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mark at least this parameter as an optional one: http://usejsdoc.org/tags-param.html#optional-parameters-and-default-values The syntax (JSDoc vs. Google Closure Compiler) should be the same as in the other repos you are working on. |
||
| */ | ||
| constructor(keyjar, config, params) { | ||
| this.keyjar = keyjar || new KeyJar(); | ||
| this.providerInfo = {}; | ||
| this.registrationResponse = {}; | ||
| this.kid = {'sig': {}, 'enc': {}}; | ||
| this.config = config || {}; | ||
| let defaultVal = ''; | ||
|
|
||
| if (params) { | ||
| for (var i = 0; i < Object.keys(params).length; i++){ | ||
| let key = Object.keys(params)[i]; | ||
| let val = params[key]; | ||
| this[key] = val; | ||
| } | ||
| } | ||
|
|
||
| this.client_id = this.config['client_id'] || defaultVal; | ||
| this.issuer = this.config['issuer'] || defaultVal; | ||
| this.client_secret = this.config['client_secret'] || defaultVal; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this line. |
||
| this.setClientSecret(this.client_secret); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Change to |
||
| this.base_url = this.config['base_url'] || defaultVal; | ||
| this.request_dir = this.config['requests_dir'] || defaultVal; | ||
|
|
||
| defaultVal = {} | ||
| this.allow = this.config['allow'] || defaultVal; | ||
| this.client_prefs = this.config['client_preferences'] || defaultVal; | ||
| this.behavior = this.config['behaviour'] || defaultVal; | ||
| this.provider_info = this.config['provider_info'] || defaultVal; | ||
|
|
||
| try { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be simplified to |
||
| this.redirectUris = this.config['redirect_uris']; | ||
| } catch (err) { | ||
| this.redirectUris = [null]; | ||
| } | ||
|
|
||
| this.callback = this.config['callback'] || {}; | ||
|
|
||
| if (config && Object.keys(config).indexOf('keydefs') !== -1) { | ||
| this.keyjar = this.keyjar.buildKeyJar(config['keydefs'], this.keyjar)[1]; | ||
| } | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| getClientSecret() { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please add a JSDoc to this function. |
||
| return this.client_secret; | ||
| } | ||
|
|
||
| setClientSecret(val) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please add a JSDoc to this function. Also mention in the JSDoc that a symmetric key is added to the keyjar and the reasons for it. |
||
| if (!val) { | ||
| this.client_secret = ''; | ||
| } else { | ||
| this.client_secret = val; | ||
| // client uses it for signing | ||
| // Server might also use it for signing which means the | ||
| // client uses it for verifying server signatures | ||
| if (this.keyjar == null) { | ||
| this.keyjar = new KeyJar(); | ||
| } | ||
| this.keyjar.addSymmetric('', val.toString()); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a unit test that verifies that a symmetric key is added to the keyjar.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On my TODO list |
||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Need to generate a redirect_uri path that is unique for a OP/RP combo | ||
| * This is to counter the mix-up attack. | ||
| * @param {string} path Leading path | ||
| * @return A list of one unique URL | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing the return type. |
||
| */ | ||
| generateRequestUris(path) { | ||
| let m = crypto.createHmac('sha256', ''); | ||
| try { | ||
| m.update(this.providerInfo['issuer']); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could probably shorten this try catch to the following: |
||
| } catch (error) { | ||
| m.update(this.issuer); | ||
| } | ||
| m.update(this.base_url); | ||
| if (!path.startsWith('/')){ | ||
| return [this.base_url + '/' + path+ '/' + m.digest('hex')]; | ||
| }else{ | ||
| return [this.base_url + path + '/' + m.digest('hex')]; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * A 1<->1 map is maintained between a URL pointing to a file and | ||
| * the name of the file in the file system. | ||
| * | ||
| * As an example if the base_url is 'https://example.com' and a jwks_uri | ||
| * is 'https://example.com/jwks_uri.json' then the filename of the | ||
| * corresponding file on the local filesystem would be 'jwks_uri.json. | ||
| * Relative to the directory from which the RP instance is run. | ||
| * | ||
| * @param {string} webName | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The description for the parameter is missing. |
||
| */ | ||
| filenameFromWebName(webName) { | ||
| if (webName.startsWith(this.base_url) == false){ | ||
| console.log('ValueError'); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that just logging and still proceeding afterwards is the right thing to do here. |
||
| } | ||
| let name = webName.substring(this.base_url.length, webName.length); | ||
| if (name.startsWith('/')) { | ||
| return name.substring(1, name.length); | ||
| } else { | ||
| let splitName = name.split('/'); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add more unit tests for this function to cover all the various cases to determine the local filename. Also cover the case, as mentioned above, when the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On my TODO list |
||
| return splitName[splitName.length - 1]; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Reformat the crypto algorithm information gathered from a | ||
| * client registration response into something more palatable. | ||
| * | ||
| * @param {string} typ: 'id_token', 'userinfo' or 'request_object' | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it really |
||
| */ | ||
| signEncAlgs(typ) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rename |
||
| let serviceContext = this; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this and replace all occurrences of |
||
| let resp = {}; | ||
| for (let i = 0; i < Object.keys(ATTRMAP[typ]).length; i++) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about using
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You might wanna check that the provided parameter really is one of the supported strings before using it, otherwise it could lead to unexpected behavior. |
||
| let key = Object.keys(ATTRMAP[typ])[i]; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. {style} Incorrect indentation. Below as well. |
||
| let val = ATTRMAP[typ][key]; | ||
| if (serviceContext.registrationResponse && serviceContext.registrationResponse[val]){ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. {style} Missing whitespace between |
||
| resp[key] = serviceContext.registrationResponse[val]; | ||
| }else if (key === 'sign') { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. {style} Missing whitespace between |
||
| try { | ||
| resp[key] = DEFAULT_SIGN_ALG[typ]; | ||
| } catch (err) { | ||
| return; | ||
| } | ||
| } | ||
| } | ||
| return resp; | ||
| } | ||
|
|
||
| /** | ||
| * Verifies that the algorithm to be used are supported by the other side. | ||
| * This will look at provider information either statically configured or | ||
| * obtained through dynamic provider info discovery. | ||
| * | ||
| * @param {string} alg The algorithm specification | ||
| * @param {string} usage In which context the 'alg' will be used. | ||
| * The following contexts are supported: | ||
| * - userinfo | ||
| * - id_token | ||
| * - request_object | ||
| * - token_endpoint_auth | ||
| * @param {string} typ Type of alg | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rename this parameter into |
||
| * - signing_alg | ||
| * - encryption_alg | ||
| * - encryption_enc | ||
| */ | ||
| verifyAlgSupport(alg, usage, typ) { | ||
| let serviceContext = this; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this and replace all occurrences of |
||
| let supported = serviceContext.providerInfo[usage + '_' + typ + '_values_supported']; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if |
||
| if (supported.indexOf(alg) !== -1) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of this |
||
| return true; | ||
| } else { | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| module.exports.ServiceContext = ServiceContext; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The module location will change once you're done with the nodeOIDCMsg repo, right?
If so, could you already require the correct and to be expected location of the module here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would the module location change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, this wasn't too clear. What I meant is whether nodeOIDCMsg becomes a dependency of the nodeOIDCService at some point that is listed in the dependency section of the
package.jsonfile? If that's the case the path in this in this require statement would slightly change.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On my todo list