@obelisk/client
Advanced tools
Comparing version 2.7.2 to 2.7.3
@@ -113,2 +113,3 @@ import { Observable, Observer } from 'rxjs'; | ||
private updateLogInfo; | ||
private storeTokens; | ||
/** | ||
@@ -115,0 +116,0 @@ * Checks a full url for a hash that can be parsed to a TokenResponse. |
@@ -25,2 +25,57 @@ "use strict"; | ||
}; | ||
this.storeTokens = (resp, hasState, offlineLoginHandling) => { | ||
if (resp.status >= 400) { | ||
this.logout(); | ||
return rxjs_1.of(false); | ||
} | ||
const authResponse = resp.response; | ||
const pat = new auth_1.Token(authResponse.access_token); | ||
const patRefresh = new auth_1.Token(authResponse.refresh_token); | ||
const idtok = new auth_1.Token(authResponse.id_token); | ||
// check nonces | ||
if (!offlineLoginHandling) { | ||
if (!this.isNonceValid(pat.getParsedToken().nonce)) { | ||
console.log('[IoT-CLIENT] Invalid nonce, clearing token'); | ||
this.clearTokens(); | ||
this.authOver$.next(); | ||
return rxjs_1.of(false); | ||
} | ||
if (!this.isNonceValid(patRefresh.getParsedToken().nonce)) { | ||
console.log('[IoT-CLIENT] Invalid nonce, clearing token'); | ||
this.clearTokens(); | ||
this.authOver$.next(); | ||
return rxjs_1.of(false); | ||
} | ||
if (!this.isNonceValid(idtok.getParsedToken().nonce)) { | ||
console.log('[IoT-CLIENT] Invalid nonce, clearing token'); | ||
this.clearTokens(); | ||
this.authOver$.next(); | ||
return rxjs_1.of(false); | ||
} | ||
} | ||
// store in memory | ||
this._tokens.pat = pat; | ||
this._tokens.patRefresh = patRefresh; | ||
this._tokens.idtoken = idtok; | ||
// If scope includes offline_access and refresh_expires_in=== 0 (store in localstorage to skip login next time) | ||
if (!offlineLoginHandling && authResponse.scope.split(' ').includes('offline_access') && authResponse.refresh_expires_in === 0) { | ||
// this._storage!.add('logInfo', { authenticated: true, expires: -1, offline_token: patRefresh.getToken() }); | ||
// console.log({token: patRefresh.getToken()}); | ||
const t = { token: patRefresh.getToken() }; | ||
this._storage.addRaw('offline', t); | ||
} | ||
// store logged in + expiration | ||
// this._storage!.add('logInfo', { authenticated: true, expires: pat.getExpiresAt() }); | ||
this.updateLogInfo(pat); | ||
// this.scheduleTokenRefresh(pat, patRefresh); | ||
this.authOver$.next(); | ||
if (hasState) { | ||
// If modern browser, insert querystring without reload | ||
if (history.pushState) { | ||
const newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + decodeURIComponent(escape(atob(decodeURIComponent(hasState)))); | ||
window.history.pushState({ path: newurl }, '', newurl); | ||
} | ||
} | ||
return rxjs_1.of(true); | ||
}; | ||
this._events$ = new rxjs_1.Subject(); | ||
@@ -56,3 +111,8 @@ // Set init mode to url or cfg based | ||
this._storage = util_1.Storage.create(cfg); | ||
}), operators_1.flatMap(options => this.getUma2Config(options)), operators_1.tap(uma2Config => this._uma2Config = uma2Config), operators_1.flatMap(_ => this.checkHashForLogin(window.location.href))); | ||
}), operators_1.flatMap(options => this.getUma2Config(options)), operators_1.tap(uma2Config => this._uma2Config = uma2Config), operators_1.flatMap(_ => this.checkHashForLogin(window.location.href)), operators_1.catchError(res => { | ||
if (res.status && res.status >= 400) { | ||
this.logout(); | ||
} | ||
return rxjs_1.of(false); | ||
})); | ||
} | ||
@@ -263,6 +323,24 @@ /** | ||
const recentlyLoggedIn = this.isLoggedIn(); | ||
if (this._tokens.pat === undefined && recentlyLoggedIn && !this._storage.get('logInfo').offline_token) { | ||
if (this._tokens.pat === undefined && recentlyLoggedIn && !this._storage.get('offline')) { | ||
console.debug('no offline token, trying silent login'); | ||
util_1.Logger.debug('No PAT and loggedIn in storage: Try to log in silently', 'AUTHN'); | ||
this.login({ prompt: 'none' }); | ||
} | ||
else { | ||
// console.log('try offline'); | ||
console.debug('offline token, trying refresh login'); | ||
const offline = this._storage.get('offline'); | ||
if (offline && offline.token) { | ||
// console.debug('--trying offline_token login'); | ||
const url = this._uma2Config.token_endpoint; | ||
const clientId = this._options.clientId; | ||
const headers = { | ||
'Content-Type': 'application/x-www-form-urlencoded', | ||
}; | ||
const params = `client_id=${clientId}&grant_type=refresh_token&refresh_token=${offline.token}`; | ||
return ajax_1.ajax.post(url, params, headers).pipe( | ||
// tap(console.log, console.log), // IN ERROR STREAM | ||
operators_1.flatMap(resp => this.storeTokens(resp, null, true))); | ||
} | ||
} | ||
if (!recentlyLoggedIn) { | ||
@@ -331,111 +409,45 @@ // Check if there are clientCredentials present | ||
} | ||
let storeTokens = (resp, hasState, offlineLoginHandling) => { | ||
if (resp.status >= 400) { | ||
return rxjs_1.of(false); | ||
} | ||
const authResponse = resp.response; | ||
const pat = new auth_1.Token(authResponse.access_token); | ||
const patRefresh = new auth_1.Token(authResponse.refresh_token); | ||
const idtok = new auth_1.Token(authResponse.id_token); | ||
// check nonces | ||
if (!offlineLoginHandling) { | ||
try { | ||
const authResponse = new auth_1.TokenResponse(url, this._options.flow); | ||
window.location.hash = ''; | ||
// Load in oauth state object if it is present | ||
this._oauth = this._storage.get('oauth', true); | ||
if (this._options.flow === 'implicit') { | ||
util_1.Logger.debug('Implicit flow', 'AUTHN'); | ||
const pat = new auth_1.Token(authResponse.access_token); | ||
const idtok = new auth_1.Token(authResponse.id_token); | ||
// check nonces | ||
if (!this.isNonceValid(pat.getParsedToken().nonce)) { | ||
console.log('[IoT-CLIENT] Invalid nonce, clearing token'); | ||
this.clearTokens(); | ||
this.authOver$.next(); | ||
return rxjs_1.of(false); | ||
} | ||
if (!this.isNonceValid(patRefresh.getParsedToken().nonce)) { | ||
console.log('[IoT-CLIENT] Invalid nonce, clearing token'); | ||
this.clearTokens(); | ||
this.authOver$.next(); | ||
return rxjs_1.of(false); | ||
} | ||
if (!this.isNonceValid(idtok.getParsedToken().nonce)) { | ||
console.log('[IoT-CLIENT] Invalid nonce, clearing token'); | ||
this.clearTokens(); | ||
this.authOver$.next(); | ||
return rxjs_1.of(false); | ||
} | ||
// store in memory | ||
this._tokens.pat = pat; | ||
this._tokens.idtoken = idtok; | ||
// store logged in + expiration | ||
// this._storage!.add('logInfo', { authenticated: true, expires: pat.getExpiresAt() }); | ||
this.updateLogInfo(pat); | ||
this.authOver$.next(); | ||
return rxjs_1.of(true); | ||
} | ||
// store in memory | ||
this._tokens.pat = pat; | ||
this._tokens.patRefresh = patRefresh; | ||
this._tokens.idtoken = idtok; | ||
// If scope includes offline_access and refresh_expires_in=== 0 (store in localstorage to skip login next time) | ||
if (!offlineLoginHandling && authResponse.scope.split(' ').includes('offline_access') && authResponse.refresh_expires_in === 0) { | ||
// this._storage!.add('logInfo', { authenticated: true, expires: -1, offline_token: patRefresh.getToken() }); | ||
this._storage.add('offline', { token: patRefresh.getToken() }); | ||
else if (this._options.flow === 'standard') { | ||
util_1.Logger.debug('Standard flow', 'AUTHN'); | ||
const tokenUrl = this._uma2Config.token_endpoint; | ||
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; | ||
const hasState = authResponse.state || null; | ||
let params = `code=${authResponse.code}&grant_type=authorization_code`; | ||
let redUri = window.location.origin + window.location.pathname; | ||
params += '&client_id=' + encodeURIComponent(this._options.clientId); | ||
params += '&redirect_uri=' + redUri; | ||
return ajax_1.ajax.post(tokenUrl, params, headers).pipe(operators_1.flatMap(resp => this.storeTokens(resp, hasState, false))); | ||
} | ||
// store logged in + expiration | ||
// this._storage!.add('logInfo', { authenticated: true, expires: pat.getExpiresAt() }); | ||
this.updateLogInfo(pat); | ||
// this.scheduleTokenRefresh(pat, patRefresh); | ||
this.authOver$.next(); | ||
if (hasState) { | ||
// If modern browser, insert querystring without reload | ||
if (history.pushState) { | ||
const newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + decodeURIComponent(escape(atob(decodeURIComponent(hasState)))); | ||
window.history.pushState({ path: newurl }, '', newurl); | ||
} | ||
} | ||
return rxjs_1.of(true); | ||
}; | ||
try { | ||
/** OFFLINE TOKEN FOUND: remembered you */ | ||
const offline = this._storage.get('offline'); | ||
// console.debug('--trying offline_token login'); | ||
if (offline && offline.token) { | ||
const url = this._uma2Config.token_endpoint; | ||
const clientId = this._options.clientId; | ||
const headers = { | ||
'Content-Type': 'application/x-www-form-urlencoded', | ||
}; | ||
const params = `client_id=${clientId}&grant_type=refresh_token&refresh_token=${offline.token}`; | ||
return ajax_1.ajax.post(url, params, headers).pipe(operators_1.flatMap(resp => storeTokens(resp, null, true))); | ||
} | ||
else { | ||
const authResponse = new auth_1.TokenResponse(url, this._options.flow); | ||
window.location.hash = ''; | ||
// Load in oauth state object if it is present | ||
this._oauth = this._storage.get('oauth', true); | ||
if (this._options.flow === 'implicit') { | ||
util_1.Logger.debug('Implicit flow', 'AUTHN'); | ||
const pat = new auth_1.Token(authResponse.access_token); | ||
const idtok = new auth_1.Token(authResponse.id_token); | ||
// check nonces | ||
if (!this.isNonceValid(pat.getParsedToken().nonce)) { | ||
console.log('[IoT-CLIENT] Invalid nonce, clearing token'); | ||
this.clearTokens(); | ||
return rxjs_1.of(false); | ||
} | ||
if (!this.isNonceValid(idtok.getParsedToken().nonce)) { | ||
console.log('[IoT-CLIENT] Invalid nonce, clearing token'); | ||
this.clearTokens(); | ||
return rxjs_1.of(false); | ||
} | ||
// store in memory | ||
this._tokens.pat = pat; | ||
this._tokens.idtoken = idtok; | ||
// store logged in + expiration | ||
// this._storage!.add('logInfo', { authenticated: true, expires: pat.getExpiresAt() }); | ||
this.updateLogInfo(pat); | ||
this.authOver$.next(); | ||
return rxjs_1.of(true); | ||
} | ||
else if (this._options.flow === 'standard') { | ||
util_1.Logger.debug('Standard flow', 'AUTHN'); | ||
const tokenUrl = this._uma2Config.token_endpoint; | ||
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; | ||
const hasState = authResponse.state || null; | ||
let params = `code=${authResponse.code}&grant_type=authorization_code`; | ||
let redUri = window.location.origin + window.location.pathname; | ||
params += '&client_id=' + encodeURIComponent(this._options.clientId); | ||
params += '&redirect_uri=' + redUri; | ||
return ajax_1.ajax.post(tokenUrl, params, headers).pipe(operators_1.flatMap(resp => storeTokens(resp, hasState, false))); | ||
} | ||
else { | ||
this.authOver$.next(); | ||
return rxjs_1.of(false); | ||
} | ||
this.authOver$.next(); | ||
return rxjs_1.of(false); | ||
} | ||
@@ -442,0 +454,0 @@ } |
@@ -19,8 +19,14 @@ import { ClientOptions } from "../interfaces"; | ||
/** | ||
* Insert the key-value pair to localStorage. | ||
* Insert the key-value pair to localStorage. Also add expires field. | ||
* @param key The key of the key-value pair | ||
* @param value The value of th ekey value pair (will be serialized to string). | ||
* @param value The value of the key value pair (will be serialized to string). | ||
*/ | ||
add(key: string, value: any): void; | ||
/** | ||
* Insert the key-value pair to localStorage. Does not add expires field! | ||
* @param key The key of the key-value pair | ||
* @param value The value of the key value pair (will be serialized to string). | ||
*/ | ||
addRaw(key: string, value: any): void; | ||
/** | ||
* Clear all entries that are saved. | ||
@@ -27,0 +33,0 @@ * Usefull to be called to logout. |
@@ -20,11 +20,21 @@ "use strict"; | ||
clearAll() { | ||
const keys = []; | ||
for (let i = 0; i < localStorage.length; i++) { | ||
let key = localStorage.key(i); | ||
const key = localStorage.key(i); | ||
if (key && key.indexOf(this.prefix) == 0) { | ||
let value = localStorage.getItem(key); | ||
if (value) { | ||
localStorage.removeItem(key); | ||
} | ||
keys.push(key); | ||
} | ||
} | ||
keys.sort((a, b) => { | ||
if (a.endsWith('logInfo')) { | ||
return 1; | ||
} | ||
else if (b.endsWith('logInfo')) { | ||
return -1; | ||
} | ||
else { | ||
return a.localeCompare(b); | ||
} | ||
}); | ||
keys.forEach(k => localStorage.removeItem(k)); | ||
} | ||
@@ -40,3 +50,3 @@ clearExpired() { | ||
let expires = JSON.parse(value).expires; | ||
if (!expires || expires < time) { | ||
if (!!expires && expires < time) { | ||
localStorage.removeItem(key); | ||
@@ -67,2 +77,6 @@ } | ||
} | ||
addRaw(key, value) { | ||
let k = this.prefix + key; | ||
localStorage.setItem(k, JSON.stringify(value)); | ||
} | ||
add(key, value) { | ||
@@ -69,0 +83,0 @@ this.clearExpired(); |
{ | ||
"name": "@obelisk/client", | ||
"version": "2.7.2", | ||
"version": "2.7.3", | ||
"description": "Typescript client to interact with Obelisk on a higher level than the regular ReST API calls.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
111347
2921