Skip to content

Commit

Permalink
Merge branch 'dev' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
brockallen authored Feb 10, 2019
2 parents 43bd23d + 27ead3b commit 84d0c7e
Show file tree
Hide file tree
Showing 13 changed files with 220 additions and 41 deletions.
6 changes: 5 additions & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ gulp.task('build-lib-sourcemap', ['jsrsasign'], function() {
entry: npmEntry,
output: {
filename:'oidc-client.js',
libraryTarget:'umd'
libraryTarget:'umd',
// Workaround for https://github.com/webpack/webpack/issues/6642
globalObject: 'this'
},
plugins: [],
devtool:'inline-source-map'
Expand All @@ -34,6 +36,8 @@ gulp.task('build-lib-min', ['jsrsasign'], function() {
output: {
filename:'oidc-client.min.js',
libraryTarget:'umd',
// Workaround for https://github.com/webpack/webpack/issues/6642
globalObject: 'this'
},
plugins: [],
devtool: false,
Expand Down
22 changes: 19 additions & 3 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export interface OidcClientSettings {
readonly staleStateAge?: number;
readonly clockSkew?: number;
readonly stateStore?: StateStore;
readonly userInfoJwtIssuer?: 'ANY' | 'OP' | string;
ResponseValidatorCtor?: ResponseValidatorCtor;
MetadataServiceCtor?: MetadataServiceCtor;
extraQueryParams?: {};
Expand Down Expand Up @@ -250,13 +251,14 @@ export class WebStorageStateStore implements StateStore {
}

export interface SigninResponse {
new (url: string): SigninResponse;
new (url: string, delimiter: string = '#'): SigninResponse;

access_token: string;
code: string;
error: string;
error_description: string;
error_uri: string;
expires_at: number;
expires_in: number;
id_token: string;
profile: any;
scope: string;
Expand All @@ -279,17 +281,31 @@ export interface SignoutResponse {
state: any;
}

export interface UserSettings {
id_token: string;
session_state: any;
access_token: string;
refresh_token: string;
token_type: string;
scope: string;
profile: any;
expires_at: number;
state: any;
}

export class User {
constructor(response: SigninResponse);
constructor(settings: UserSettings);

id_token: string;
session_state: any;
access_token: string;
refresh_token: string;
token_type: string;
scope: string;
profile: any;
expires_at: number;
state: any;

toStorageString(): string;

readonly expires_in: number | undefined;
Expand Down
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,13 @@
"gulp-concat": "^2.6.1",
"mocha": "^5.2.0",
"natives": "^1.1.6",
"open": "0.0.5",
"uglifyjs-webpack-plugin": "^1.2.7",
"webpack": "^4.16.0",
"webpack-stream": "^4.0.3"
},
"dependencies": {
"jsrsasign": "^8.0.12"
},
"optionalDependencies": {
"babel-polyfill": ">=6.9.1"
},
"typings": "index.d.ts"
}
54 changes: 28 additions & 26 deletions src/JoseUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class JoseUtil {
}
}

static validateJwt(jwt, key, issuer, audience, clockSkew, now) {
static validateJwt(jwt, key, issuer, audience, clockSkew, now, timeInsensitive) {
Log.debug("JoseUtil.validateJwt");

try {
Expand Down Expand Up @@ -54,15 +54,15 @@ export class JoseUtil {
return Promise.reject(new Error("Unsupported key type: " + key && key.kty));
}

return JoseUtil._validateJwt(jwt, key, issuer, audience, clockSkew, now);
return JoseUtil._validateJwt(jwt, key, issuer, audience, clockSkew, now, timeInsensitive);
}
catch (e) {
Log.error(e && e.message || e);
return Promise.reject("JWT validation failed");
}
}

static validateJwtAttributes(jwt, issuer, audience, clockSkew, now) {
static validateJwtAttributes(jwt, issuer, audience, clockSkew, now, timeInsensitive) {
if (!clockSkew) {
clockSkew = 0;
}
Expand Down Expand Up @@ -96,38 +96,40 @@ export class JoseUtil {
return Promise.reject(new Error("Invalid azp in token: " + payload.azp));
}

var lowerNow = now + clockSkew;
var upperNow = now - clockSkew;
if (!timeInsensitive) {
var lowerNow = now + clockSkew;
var upperNow = now - clockSkew;

if (!payload.iat) {
Log.error("JoseUtil._validateJwt: iat was not provided");
return Promise.reject(new Error("iat was not provided"));
}
if (lowerNow < payload.iat) {
Log.error("JoseUtil._validateJwt: iat is in the future", payload.iat);
return Promise.reject(new Error("iat is in the future: " + payload.iat));
}
if (!payload.iat) {
Log.error("JoseUtil._validateJwt: iat was not provided");
return Promise.reject(new Error("iat was not provided"));
}
if (lowerNow < payload.iat) {
Log.error("JoseUtil._validateJwt: iat is in the future", payload.iat);
return Promise.reject(new Error("iat is in the future: " + payload.iat));
}

if (payload.nbf && lowerNow < payload.nbf) {
Log.error("JoseUtil._validateJwt: nbf is in the future", payload.nbf);
return Promise.reject(new Error("nbf is in the future: " + payload.nbf));
}
if (payload.nbf && lowerNow < payload.nbf) {
Log.error("JoseUtil._validateJwt: nbf is in the future", payload.nbf);
return Promise.reject(new Error("nbf is in the future: " + payload.nbf));
}

if (!payload.exp) {
Log.error("JoseUtil._validateJwt: exp was not provided");
return Promise.reject(new Error("exp was not provided"));
}
if (payload.exp < upperNow) {
Log.error("JoseUtil._validateJwt: exp is in the past", payload.exp);
return Promise.reject(new Error("exp is in the past:" + payload.exp));
if (!payload.exp) {
Log.error("JoseUtil._validateJwt: exp was not provided");
return Promise.reject(new Error("exp was not provided"));
}
if (payload.exp < upperNow) {
Log.error("JoseUtil._validateJwt: exp is in the past", payload.exp);
return Promise.reject(new Error("exp is in the past:" + payload.exp));
}
}

return Promise.resolve(payload);
}

static _validateJwt(jwt, key, issuer, audience, clockSkew, now) {
static _validateJwt(jwt, key, issuer, audience, clockSkew, now, timeInsensitive) {

return JoseUtil.validateJwtAttributes(jwt, issuer, audience, clockSkew, now).then(payload => {
return JoseUtil.validateJwtAttributes(jwt, issuer, audience, clockSkew, now, timeInsensitive).then(payload => {
try {
if (!jws.JWS.verify(jwt, key, AllowedSigningAlgs)) {
Log.error("JoseUtil._validateJwt: signature validation failed");
Expand Down
16 changes: 15 additions & 1 deletion src/JsonService.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { Log } from './Log.js';
import { Global } from './Global.js';

export class JsonService {
constructor(additionalContentTypes = null, XMLHttpRequestCtor = Global.XMLHttpRequest) {
constructor(
additionalContentTypes = null,
XMLHttpRequestCtor = Global.XMLHttpRequest,
jwtHandler = null
) {
if (additionalContentTypes && Array.isArray(additionalContentTypes))
{
this._contentTypes = additionalContentTypes.slice();
Expand All @@ -15,8 +19,12 @@ export class JsonService {
this._contentTypes = [];
}
this._contentTypes.push('application/json');
if (jwtHandler) {
this._contentTypes.push('application/jwt');
}

this._XMLHttpRequest = XMLHttpRequestCtor;
this._jwtHandler = jwtHandler;
}

getJson(url, token) {
Expand All @@ -33,6 +41,7 @@ export class JsonService {
req.open('GET', url);

var allowedContentTypes = this._contentTypes;
var jwtHandler = this._jwtHandler;

req.onload = function() {
Log.debug("JsonService.getJson: HTTP response received, status", req.status);
Expand All @@ -48,6 +57,11 @@ export class JsonService {
}
});

if (found == "application/jwt") {
jwtHandler(req).then(resolve, reject);
return;
}

if (found) {
try {
resolve(JSON.parse(req.responseText));
Expand Down
6 changes: 4 additions & 2 deletions src/OidcClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,13 @@ export class OidcClient {
});
}

createSignoutRequest({id_token_hint, data, state, post_logout_redirect_uri} = {},
createSignoutRequest({id_token_hint, data, state, post_logout_redirect_uri, extraQueryParams } = {},
stateStore
) {
Log.debug("OidcClient.createSignoutRequest");

post_logout_redirect_uri = post_logout_redirect_uri || this._settings.post_logout_redirect_uri;
extraQueryParams = extraQueryParams || this._settings.extraQueryParams;

return this._metadataService.getEndSessionEndpoint().then(url => {
if (!url) {
Expand All @@ -142,7 +143,8 @@ export class OidcClient {
url,
id_token_hint,
post_logout_redirect_uri,
data: data || state
data: data || state,
extraQueryParams
});

var signoutState = request.state;
Expand Down
5 changes: 5 additions & 0 deletions src/OidcClientSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export class OidcClientSettings {
// behavior flags
filterProtocolClaims = true, loadUserInfo = true,
staleStateAge = DefaultStaleStateAge, clockSkew = DefaultClockSkewInSeconds,
userInfoJwtIssuer = 'OP',
// other behavior
stateStore = new WebStorageStateStore(),
ResponseValidatorCtor = ResponseValidator,
Expand Down Expand Up @@ -57,6 +58,7 @@ export class OidcClientSettings {
this._loadUserInfo = !!loadUserInfo;
this._staleStateAge = staleStateAge;
this._clockSkew = clockSkew;
this._userInfoJwtIssuer = userInfoJwtIssuer;

this._stateStore = stateStore;
this._validator = new ResponseValidatorCtor(this);
Expand Down Expand Up @@ -177,6 +179,9 @@ export class OidcClientSettings {
get clockSkew() {
return this._clockSkew;
}
get userInfoJwtIssuer() {
return this._userInfoJwtIssuer;
}

get stateStore() {
return this._stateStore;
Expand Down
7 changes: 6 additions & 1 deletion src/ResponseValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,12 @@ export class ResponseValidator {
}
}
else if (result[name] !== value) {
result[name] = [result[name], value];
if (typeof value === 'object') {
result[name] = this._mergeClaims(result[name], value);
}
else {
result[name] = [result[name], value];
}
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/SignoutRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { UrlUtility } from './UrlUtility.js';
import { State } from './State.js';

export class SignoutRequest {
constructor({url, id_token_hint, post_logout_redirect_uri, data}) {
constructor({url, id_token_hint, post_logout_redirect_uri, data, extraQueryParams}) {
if (!url) {
Log.error("SignoutRequest.ctor: No url passed");
throw new Error("url");
Expand All @@ -26,6 +26,10 @@ export class SignoutRequest {
}
}

for(let key in extraQueryParams){
url = UrlUtility.addQueryParam(url, key, extraQueryParams[key])
}

this.url = url;
}
}
Loading

0 comments on commit 84d0c7e

Please sign in to comment.