secure state, api fix for discover(), url trailing slash fix
This commit is contained in:
parent
9962c72e60
commit
a43282fab6
|
@ -73,20 +73,25 @@
|
||||||
if (!providerUri) {
|
if (!providerUri) {
|
||||||
throw new Error("cannot discover without providerUri");
|
throw new Error("cannot discover without providerUri");
|
||||||
}
|
}
|
||||||
if (!opts.state) {
|
|
||||||
throw new Error("cannot discover without opts.state");
|
|
||||||
}
|
|
||||||
if (!opts.appUrl) {
|
if (!opts.appUrl) {
|
||||||
throw new Error("cannot discover without opts.appUrl");
|
throw new Error("cannot discover without opts.appUrl");
|
||||||
}
|
}
|
||||||
|
|
||||||
var params = {
|
var params = {
|
||||||
action: 'directives'
|
action: 'directives'
|
||||||
, state: opts.state
|
, state: core.utils.randomState()
|
||||||
, redirect_uri: opts.appUrl + (opts.appCallbackPath || '/.well-known/oauth3/callback.html')
|
, redirect_uri: opts.appUrl + (opts.appCallbackPath || '/.well-known/oauth3/callback.html')
|
||||||
|
, debug: opts.debug || undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
return providerUri + '/.well-known/oauth3/directives.html#' + core.querystringify(params);
|
var result = {
|
||||||
|
url: providerUri + '/.well-known/oauth3/directives.html#' + core.querystringify(params)
|
||||||
|
, state: params.state
|
||||||
|
, method: 'GET'
|
||||||
|
, query: params
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
// these might not really belong in core... not sure
|
// these might not really belong in core... not sure
|
||||||
|
@ -104,6 +109,29 @@
|
||||||
b64 = b64.replace(/=+/g, '');
|
b64 = b64.replace(/=+/g, '');
|
||||||
return b64;
|
return b64;
|
||||||
}
|
}
|
||||||
|
, randomState: function () {
|
||||||
|
var i;
|
||||||
|
var ch;
|
||||||
|
var str;
|
||||||
|
|
||||||
|
// TODO put in different file for browser vs node
|
||||||
|
try {
|
||||||
|
return Array.prototype.slice.call(window.crypto.getRandomValues(new Uint8Array(16))).map(function (ch) { return (ch).toString(16); }).join('');
|
||||||
|
} catch(e) {
|
||||||
|
// TODO use fisher-yates on 0..255 and select [0] 16 times
|
||||||
|
// [security] https://medium.com/@betable/tifu-by-using-math-random-f1c308c4fd9d#.5qx0bf95a
|
||||||
|
// https://github.com/v8/v8/blob/b0e4dce6091a8777bda80d962df76525dc6c5ea9/src/js/math.js#L135-L144
|
||||||
|
// Note: newer versions of v8 do not have this bug, but other engines may still
|
||||||
|
console.warn('[security] crypto.getRandomValues() failed, falling back to Math.random()');
|
||||||
|
str = '';
|
||||||
|
for (i = 0; i < 32; i += 1) {
|
||||||
|
ch = Math.round(Math.random() * 255).toString(16);
|
||||||
|
if (ch.length < 2) { ch = '0' + ch; }
|
||||||
|
str += ch;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
core.jwt = {
|
core.jwt = {
|
||||||
// decode only (no verification)
|
// decode only (no verification)
|
||||||
|
@ -149,7 +177,7 @@
|
||||||
// GET https://example.com/api/org.oauth3.provider/authorization_dialog
|
// GET https://example.com/api/org.oauth3.provider/authorization_dialog
|
||||||
// ?response_type=code
|
// ?response_type=code
|
||||||
// &scope=`encodeURIComponent('profile.login profile.email')`
|
// &scope=`encodeURIComponent('profile.login profile.email')`
|
||||||
// &state=`Math.random()`
|
// &state=`cryptoutil.random().toString('hex')`
|
||||||
// &client_id=xxxxxxxxxxx
|
// &client_id=xxxxxxxxxxx
|
||||||
// &redirect_uri=`encodeURIComponent('https://myapp.com/oauth3.html')`
|
// &redirect_uri=`encodeURIComponent('https://myapp.com/oauth3.html')`
|
||||||
//
|
//
|
||||||
|
@ -175,7 +203,7 @@
|
||||||
// &scope=`encodeURIComponent('profile.login profile.email')`
|
// &scope=`encodeURIComponent('profile.login profile.email')`
|
||||||
//
|
//
|
||||||
// (optional)
|
// (optional)
|
||||||
// &state=`Math.random()`
|
// &state=`cryptoutil.random().toString('hex')`
|
||||||
// &redirect_uri=`encodeURIComponent('https://myapp.com/oauth3.html')`
|
// &redirect_uri=`encodeURIComponent('https://myapp.com/oauth3.html')`
|
||||||
//
|
//
|
||||||
// NOTE: This is not a request sent to the provider, but rather a request sent to the
|
// NOTE: This is not a request sent to the provider, but rather a request sent to the
|
||||||
|
@ -186,12 +214,12 @@
|
||||||
|
|
||||||
var scope = opts.scope || directive.authn_scope;
|
var scope = opts.scope || directive.authn_scope;
|
||||||
var providerUri = directive.provider_uri;
|
var providerUri = directive.provider_uri;
|
||||||
|
var params = {
|
||||||
var state = Math.random().toString().replace(/^0\./, '');
|
state: core.utils.randomState()
|
||||||
var params = {};
|
, debug: opts.debug || undefined
|
||||||
|
};
|
||||||
var slimProviderUri = encodeURIComponent(providerUri.replace(/^(https?|spdy):\/\//, ''));
|
var slimProviderUri = encodeURIComponent(providerUri.replace(/^(https?|spdy):\/\//, ''));
|
||||||
|
|
||||||
params.state = state;
|
|
||||||
if (scope) {
|
if (scope) {
|
||||||
params.scope = scope;
|
params.scope = scope;
|
||||||
}
|
}
|
||||||
|
@ -216,7 +244,7 @@
|
||||||
return {
|
return {
|
||||||
url: authorizationRedirect + '?' + core.querystringify(params)
|
url: authorizationRedirect + '?' + core.querystringify(params)
|
||||||
, method: 'GET'
|
, method: 'GET'
|
||||||
, state: state // this becomes browser_state
|
, state: params.state // this becomes browser_state
|
||||||
, params: params // includes scope, final redirect_uri?
|
, params: params // includes scope, final redirect_uri?
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -230,7 +258,7 @@
|
||||||
// GET https://example.com/api/org.oauth3.provider/authorization_dialog
|
// GET https://example.com/api/org.oauth3.provider/authorization_dialog
|
||||||
// ?response_type=token
|
// ?response_type=token
|
||||||
// &scope=`encodeURIComponent('profile.login profile.email')`
|
// &scope=`encodeURIComponent('profile.login profile.email')`
|
||||||
// &state=`Math.random()`
|
// &state=`cryptoutil.random().toString('hex')`
|
||||||
// &client_id=xxxxxxxxxxx
|
// &client_id=xxxxxxxxxxx
|
||||||
// &redirect_uri=`encodeURIComponent('https://myapp.com/oauth3.html')`
|
// &redirect_uri=`encodeURIComponent('https://myapp.com/oauth3.html')`
|
||||||
//
|
//
|
||||||
|
@ -246,8 +274,10 @@
|
||||||
var clientId = opts.appId || opts.clientId;
|
var clientId = opts.appId || opts.clientId;
|
||||||
var args = directive[type];
|
var args = directive[type];
|
||||||
var uri = args.url;
|
var uri = args.url;
|
||||||
var state = Math.random().toString().replace(/^0\./, '');
|
var state = core.utils.randomState();
|
||||||
var params = {};
|
var params = {
|
||||||
|
debug: opts.debug || undefined
|
||||||
|
};
|
||||||
var loc;
|
var loc;
|
||||||
var result;
|
var result;
|
||||||
|
|
||||||
|
@ -303,6 +333,7 @@
|
||||||
"username": opts.id || opts.username
|
"username": opts.id || opts.username
|
||||||
, "request_otp": true // opts.requestOtp || undefined
|
, "request_otp": true // opts.requestOtp || undefined
|
||||||
//, "jwt": opts.jwt // TODO sign a proof
|
//, "jwt": opts.jwt // TODO sign a proof
|
||||||
|
, debug: opts.debug || undefined
|
||||||
};
|
};
|
||||||
var uri = args.url;
|
var uri = args.url;
|
||||||
var body;
|
var body;
|
||||||
|
@ -365,6 +396,7 @@
|
||||||
//, "public_key": opts.rememberDevice && opts.publicKey || undefined
|
//, "public_key": opts.rememberDevice && opts.publicKey || undefined
|
||||||
//, "public_key_type": opts.rememberDevice && opts.publicKeyType || undefined // RSA/ECDSA
|
//, "public_key_type": opts.rememberDevice && opts.publicKeyType || undefined // RSA/ECDSA
|
||||||
//, "jwt": opts.jwt // TODO sign a proof with a previously loaded public_key
|
//, "jwt": opts.jwt // TODO sign a proof with a previously loaded public_key
|
||||||
|
, debug: opts.debug || undefined
|
||||||
};
|
};
|
||||||
var uri = args.url;
|
var uri = args.url;
|
||||||
var body;
|
var body;
|
||||||
|
@ -426,6 +458,7 @@
|
||||||
//, "client_uri": undefined
|
//, "client_uri": undefined
|
||||||
//, "scope": undefined
|
//, "scope": undefined
|
||||||
//, "client_secret": undefined
|
//, "client_secret": undefined
|
||||||
|
, debug: opts.debug || undefined
|
||||||
};
|
};
|
||||||
var uri = args.url;
|
var uri = args.url;
|
||||||
var body;
|
var body;
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
console.warn('[deprecated] using window.location.{protocol, host, pathname} when opts.appUrl should be used');
|
console.warn('[deprecated] using window.location.{protocol, host, pathname} when opts.appUrl should be used');
|
||||||
return window.location.protocol
|
return window.location.protocol
|
||||||
+ '//' + window.location.host
|
+ '//' + window.location.host
|
||||||
+ (window.location.pathname).replace(/\/?$/, '/')
|
+ (window.location.pathname).replace(/\/?$/, '')
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue