convert to identityserver auth

This commit is contained in:
Kyle Spearrin 2017-01-17 23:07:46 -05:00
parent f84bfcb19a
commit 6bb6c7074b
6 changed files with 202 additions and 83 deletions

View File

@ -15,10 +15,29 @@ var FolderRequest = function (folder) {
var TokenRequest = function (email, masterPasswordHash, device) { var TokenRequest = function (email, masterPasswordHash, device) {
this.email = email; this.email = email;
this.masterPasswordHash = masterPasswordHash; this.masterPasswordHash = masterPasswordHash;
this.device = null;
if (device) { if (device) {
this.device = new DeviceRequest(device); this.device = new DeviceRequest(device);
} }
this.device = null;
this.toIdentityToken = function () {
var obj = {
grant_type: 'password',
username: this.email,
password: this.masterPasswordHash,
scope: 'api offline_access',
client_id: 'browser'
};
if (this.device) {
obj.deviceType = this.device.type;
obj.deviceIdentifier = this.device.identifier;
obj.deviceName = this.device.name;
obj.devicePushToken = this.device.pushToken;
}
return obj;
};
}; };
var RegisterRequest = function (email, masterPasswordHash, masterPasswordHint) { var RegisterRequest = function (email, masterPasswordHash, masterPasswordHint) {

View File

@ -46,6 +46,15 @@ var TokenResponse = function (response) {
} }
}; };
var IdentityTokenResponse = function (response) {
this.accessToken = response.access_token;
this.expiresIn = response.expires_in;
this.refreshToken = response.refresh_token;
this.tokenType = response.token_type;
// TODO: extras
};
var ListResponse = function (data) { var ListResponse = function (data) {
this.data = data; this.data = data;
}; };

View File

@ -197,35 +197,33 @@
} }
cryptoService.getKey(false, function (key) { cryptoService.getKey(false, function (key) {
tokenService.getToken(function (token) { userService.isAuthenticated(function (isAuthenticated) {
userService.isAuthenticated(function (isAuthenticated) { if (isAuthenticated) {
if (isAuthenticated) { var obj = {};
var obj = {}; obj[constantsService.lastActiveKey] = (new Date()).getTime();
obj[constantsService.lastActiveKey] = (new Date()).getTime(); chrome.storage.local.set(obj, function () { });
chrome.storage.local.set(obj, function () { }); }
}
if (!toState.data || !toState.data.authorize) { if (!toState.data || !toState.data.authorize) {
if (isAuthenticated && !tokenService.isTokenExpired(token)) { if (isAuthenticated && !tokenService.isTokenExpired()) {
event.preventDefault();
if (!key) {
$state.go('lock');
}
else {
$state.go('tabs.current');
}
}
return;
}
if (!isAuthenticated || tokenService.isTokenExpired(token)) {
event.preventDefault(); event.preventDefault();
authService.logOut(function () { if (!key) {
$state.go('home'); $state.go('lock');
}); }
else {
$state.go('tabs.current');
}
} }
});
return;
}
if (!isAuthenticated || tokenService.isTokenExpired()) {
event.preventDefault();
authService.logOut(function () {
$state.go('home');
});
}
}); });
}); });
}); });

View File

@ -12,25 +12,25 @@
cryptoService.hashPassword(masterPassword, key, function (hashedPassword) { cryptoService.hashPassword(masterPassword, key, function (hashedPassword) {
var request = new TokenRequest(email, hashedPassword); var request = new TokenRequest(email, hashedPassword);
apiService.postToken(request, function (response) { apiService.postIdentityToken(request, function (response) {
if (!response || !response.token) { if (!response || !response.accessToken) {
return; return;
} }
tokenService.setToken(response.token, function () { tokenService.setTokens(response.accessToken, response.refreshToken, function () {
cryptoService.setKey(key, function () { cryptoService.setKey(key, function () {
cryptoService.setKeyHash(hashedPassword, function () { cryptoService.setKeyHash(hashedPassword, function () {
if (response.profile) { if (tokenService.isTwoFactorScheme()) {
userService.setUserId(response.profile.id, function () { deferred.resolve(response);
userService.setEmail(response.profile.email, function () { }
else {
userService.setUserId(tokenService.getUserId(), function () {
userService.setEmail(tokenService.getEmail(), function () {
chrome.runtime.sendMessage({ command: 'loggedIn' }); chrome.runtime.sendMessage({ command: 'loggedIn' });
deferred.resolve(response); deferred.resolve(response);
}); });
}); });
} }
else {
deferred.resolve(response);
}
}); });
}); });
}); });

View File

@ -8,29 +8,12 @@ function ApiService(tokenService) {
function initApiService() { function initApiService() {
// Auth APIs // Auth APIs
ApiService.prototype.postToken = function (tokenRequest, success, error) {
var self = this;
$.ajax({
type: 'POST',
url: self.baseUrl + '/auth/token',
data: JSON.stringify(tokenRequest),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function (response) {
success(new TokenResponse(response));
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
}
});
};
ApiService.prototype.postTokenTwoFactor = function (twoFactorTokenRequest, success, error) { ApiService.prototype.postTokenTwoFactor = function (twoFactorTokenRequest, success, error) {
var self = this; var self = this;
this.tokenService.getToken(function (token) { this.tokenService.getToken(function (token) {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: self.baseUrl + '/auth/token/two-factor?access_token=' + token, url: self.baseUrl + '/auth/token/two-factor?access_token2=' + token,
data: JSON.stringify(twoFactorTokenRequest), data: JSON.stringify(twoFactorTokenRequest),
contentType: 'application/json; charset=utf-8', contentType: 'application/json; charset=utf-8',
dataType: 'json', dataType: 'json',
@ -44,6 +27,24 @@ function initApiService() {
}); });
}; };
ApiService.prototype.postIdentityToken = function (tokenRequest, success, error) {
var self = this;
$.ajax({
type: 'POST',
url: self.baseUrl + '/connect/token',
data: tokenRequest.toIdentityToken(),
contentType: 'application/x-www-form-urlencoded; charset=utf-8',
dataType: 'json',
success: function (response) {
success(new IdentityTokenResponse(response));
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
}
});
};
// Account APIs // Account APIs
ApiService.prototype.getAccountRevisionDate = function (success, error) { ApiService.prototype.getAccountRevisionDate = function (success, error) {
@ -51,7 +52,7 @@ function initApiService() {
this.tokenService.getToken(function (token) { this.tokenService.getToken(function (token) {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: self.baseUrl + '/accounts/revision-date?access_token=' + token, url: self.baseUrl + '/accounts/revision-date?access_token2=' + token,
dataType: 'json', dataType: 'json',
success: function (response) { success: function (response) {
success(response); success(response);
@ -68,7 +69,7 @@ function initApiService() {
this.tokenService.getToken(function (token) { this.tokenService.getToken(function (token) {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: self.baseUrl + '/accounts/profile?access_token=' + token, url: self.baseUrl + '/accounts/profile?access_token2=' + token,
dataType: 'json', dataType: 'json',
success: function (response) { success: function (response) {
success(new ProfileResponse(response)); success(new ProfileResponse(response));
@ -121,7 +122,7 @@ function initApiService() {
this.tokenService.getToken(function (token) { this.tokenService.getToken(function (token) {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: self.baseUrl + '/settings/domains?excluded=false&access_token=' + token, url: self.baseUrl + '/settings/domains?excluded=false&access_token2=' + token,
dataType: 'json', dataType: 'json',
success: function (response) { success: function (response) {
success(new DomainsResponse(response)); success(new DomainsResponse(response));
@ -140,7 +141,7 @@ function initApiService() {
this.tokenService.getToken(function (token) { this.tokenService.getToken(function (token) {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: self.baseUrl + '/sites/' + id + '?access_token=' + token, url: self.baseUrl + '/sites/' + id + '?access_token2=' + token,
dataType: 'json', dataType: 'json',
success: function (response) { success: function (response) {
success(new LoginResponse(response)); success(new LoginResponse(response));
@ -157,7 +158,7 @@ function initApiService() {
this.tokenService.getToken(function (token) { this.tokenService.getToken(function (token) {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: self.baseUrl + '/sites?access_token=' + token, url: self.baseUrl + '/sites?access_token2=' + token,
data: JSON.stringify(loginRequest), data: JSON.stringify(loginRequest),
contentType: 'application/json; charset=utf-8', contentType: 'application/json; charset=utf-8',
dataType: 'json', dataType: 'json',
@ -176,7 +177,7 @@ function initApiService() {
this.tokenService.getToken(function (token) { this.tokenService.getToken(function (token) {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: self.baseUrl + '/sites/' + id + '?access_token=' + token, url: self.baseUrl + '/sites/' + id + '?access_token2=' + token,
data: JSON.stringify(loginRequest), data: JSON.stringify(loginRequest),
contentType: 'application/json; charset=utf-8', contentType: 'application/json; charset=utf-8',
dataType: 'json', dataType: 'json',
@ -197,7 +198,7 @@ function initApiService() {
this.tokenService.getToken(function (token) { this.tokenService.getToken(function (token) {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: self.baseUrl + '/folders/' + id + '?access_token=' + token, url: self.baseUrl + '/folders/' + id + '?access_token2=' + token,
dataType: 'json', dataType: 'json',
success: function (response) { success: function (response) {
success(new FolderResponse(response)); success(new FolderResponse(response));
@ -214,7 +215,7 @@ function initApiService() {
this.tokenService.getToken(function (token) { this.tokenService.getToken(function (token) {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: self.baseUrl + '/folders?access_token=' + token, url: self.baseUrl + '/folders?access_token2=' + token,
data: JSON.stringify(folderRequest), data: JSON.stringify(folderRequest),
contentType: 'application/json; charset=utf-8', contentType: 'application/json; charset=utf-8',
dataType: 'json', dataType: 'json',
@ -233,7 +234,7 @@ function initApiService() {
this.tokenService.getToken(function (token) { this.tokenService.getToken(function (token) {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: self.baseUrl + '/folders/' + id + '?access_token=' + token, url: self.baseUrl + '/folders/' + id + '?access_token2=' + token,
data: JSON.stringify(folderRequest), data: JSON.stringify(folderRequest),
contentType: 'application/json; charset=utf-8', contentType: 'application/json; charset=utf-8',
dataType: 'json', dataType: 'json',
@ -254,7 +255,7 @@ function initApiService() {
this.tokenService.getToken(function (token) { this.tokenService.getToken(function (token) {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: self.baseUrl + '/ciphers/' + id + '?access_token=' + token, url: self.baseUrl + '/ciphers/' + id + '?access_token2=' + token,
dataType: 'json', dataType: 'json',
success: function (response) { success: function (response) {
success(new CipherResponse(response)); success(new CipherResponse(response));
@ -271,7 +272,7 @@ function initApiService() {
this.tokenService.getToken(function (token) { this.tokenService.getToken(function (token) {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: self.baseUrl + '/ciphers?access_token=' + token, url: self.baseUrl + '/ciphers?access_token2=' + token,
dataType: 'json', dataType: 'json',
success: function (response) { success: function (response) {
var data = []; var data = [];
@ -293,7 +294,7 @@ function initApiService() {
this.tokenService.getToken(function (token) { this.tokenService.getToken(function (token) {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: self.baseUrl + '/ciphers/' + id + '/delete?access_token=' + token, url: self.baseUrl + '/ciphers/' + id + '/delete?access_token2=' + token,
dataType: 'text', dataType: 'text',
success: function (response) { success: function (response) {
success(); success();

View File

@ -3,7 +3,22 @@
}; };
function initTokenService() { function initTokenService() {
var _token; var _token,
_decodedToken,
_refreshToken;
TokenService.prototype.setTokens = function (accessToken, refreshToken, callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
var self = this;
self.setToken(accessToken, function () {
self.setRefreshToken(refreshToken, function () {
callback();
});
});
};
TokenService.prototype.setToken = function (token, callback) { TokenService.prototype.setToken = function (token, callback) {
if (!callback || typeof callback !== 'function') { if (!callback || typeof callback !== 'function') {
@ -11,8 +26,9 @@ function initTokenService() {
} }
_token = token; _token = token;
_decodedToken = null;
chrome.storage.local.set({ chrome.storage.local.set({
'authBearer': token 'accessToken': token
}, function () { }, function () {
callback(); callback();
}); });
@ -27,48 +43,88 @@ function initTokenService() {
return callback(_token); return callback(_token);
} }
chrome.storage.local.get('authBearer', function (obj) { chrome.storage.local.get('accessToken', function (obj) {
if (obj && obj.authBearer) { if (obj && obj.accessToken) {
_token = obj.authBearer; _token = obj.accessToken;
} }
return callback(_token); return callback(_token);
}); });
}; };
TokenService.prototype.setRefreshToken = function (refreshToken, callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
_refreshToken = refreshToken;
chrome.storage.local.set({
'refreshToken': refreshToken
}, function () {
callback();
});
};
TokenService.prototype.getRefreshToken = function (callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
if (_refreshToken) {
return callback(_refreshToken);
}
chrome.storage.local.get('refreshToken', function (obj) {
if (obj && obj.refreshToken) {
_refreshToken = obj.refreshToken;
}
return callback(_refreshToken);
});
};
TokenService.prototype.clearToken = function (callback) { TokenService.prototype.clearToken = function (callback) {
if (!callback || typeof callback !== 'function') { if (!callback || typeof callback !== 'function') {
throw 'callback function required'; throw 'callback function required';
} }
_token = null; _token = _decodedToken = _refreshToken = null;
chrome.storage.local.remove('authBearer', function () { chrome.storage.local.remove('accessToken', function () {
callback(); chrome.storage.local.remove('refreshToken', function () {
callback();
});
}); });
}; };
// jwthelper methods // jwthelper methods
// ref https://github.com/auth0/angular-jwt/blob/master/src/angularJwt/services/jwt.js // ref https://github.com/auth0/angular-jwt/blob/master/src/angularJwt/services/jwt.js
TokenService.prototype.decodeToken = function (token) { TokenService.prototype.decodeToken = function () {
var parts = token.split('.'); if (_decodedToken) {
return _decodedToken;
}
if (!_token) {
throw 'Token not found.';
}
var parts = _token.split('.');
if (parts.length !== 3) { if (parts.length !== 3) {
throw new Error('JWT must have 3 parts'); throw 'JWT must have 3 parts';
} }
var decoded = urlBase64Decode(parts[1]); var decoded = urlBase64Decode(parts[1]);
if (!decoded) { if (!decoded) {
throw new Error('Cannot decode the token'); throw 'Cannot decode the token';
} }
return JSON.parse(decoded); return JSON.parse(decoded);
}; };
TokenService.prototype.getTokenExpirationDate = function (token) { TokenService.prototype.getTokenExpirationDate = function () {
var decoded = this.decodeToken(token); var decoded = this.decodeToken();
if (typeof decoded.exp === "undefined") { if (typeof decoded.exp === 'undefined') {
return null; return null;
} }
@ -78,8 +134,8 @@ function initTokenService() {
return d; return d;
}; };
TokenService.prototype.isTokenExpired = function (token, offsetSeconds) { TokenService.prototype.isTokenExpired = function (offsetSeconds) {
var d = this.getTokenExpirationDate(token); var d = this.getTokenExpirationDate();
offsetSeconds = offsetSeconds || 0; offsetSeconds = offsetSeconds || 0;
if (d === null) { if (d === null) {
return false; return false;
@ -89,6 +145,42 @@ function initTokenService() {
return !(d.valueOf() > (new Date().valueOf() + (offsetSeconds * 1000))); return !(d.valueOf() > (new Date().valueOf() + (offsetSeconds * 1000)));
}; };
TokenService.prototype.isTwoFactorScheme = function () {
return this.getScheme() !== 'Application';
};
TokenService.prototype.getScheme = function () {
var decoded = this.decodeToken();
if (typeof decoded.amr === 'undefined' || !decoded.amr.length) {
throw 'No scheme found';
}
return decoded.amr[0];
};
TokenService.prototype.getUserId = function () {
var decoded = this.decodeToken();
if (typeof decoded.sub === 'undefined') {
throw 'No user id found';
}
return decoded.sub;
};
TokenService.prototype.getEmail = function () {
var decoded = this.decodeToken();
var email = decoded['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'];
if (typeof email === 'undefined') {
throw 'No email found';
}
return email;
};
function urlBase64Decode(str) { function urlBase64Decode(str) {
var output = str.replace(/-/g, '+').replace(/_/g, '/'); var output = str.replace(/-/g, '+').replace(/_/g, '/');
switch (output.length % 4) { switch (output.length % 4) {