1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-12-29 17:38:04 +01:00

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) {
this.email = email;
this.masterPasswordHash = masterPasswordHash;
this.device = null;
if (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) {

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) {
this.data = data;
};

View File

@ -197,35 +197,33 @@
}
cryptoService.getKey(false, function (key) {
tokenService.getToken(function (token) {
userService.isAuthenticated(function (isAuthenticated) {
if (isAuthenticated) {
var obj = {};
obj[constantsService.lastActiveKey] = (new Date()).getTime();
chrome.storage.local.set(obj, function () { });
}
userService.isAuthenticated(function (isAuthenticated) {
if (isAuthenticated) {
var obj = {};
obj[constantsService.lastActiveKey] = (new Date()).getTime();
chrome.storage.local.set(obj, function () { });
}
if (!toState.data || !toState.data.authorize) {
if (isAuthenticated && !tokenService.isTokenExpired(token)) {
event.preventDefault();
if (!key) {
$state.go('lock');
}
else {
$state.go('tabs.current');
}
}
return;
}
if (!isAuthenticated || tokenService.isTokenExpired(token)) {
if (!toState.data || !toState.data.authorize) {
if (isAuthenticated && !tokenService.isTokenExpired()) {
event.preventDefault();
authService.logOut(function () {
$state.go('home');
});
if (!key) {
$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) {
var request = new TokenRequest(email, hashedPassword);
apiService.postToken(request, function (response) {
if (!response || !response.token) {
apiService.postIdentityToken(request, function (response) {
if (!response || !response.accessToken) {
return;
}
tokenService.setToken(response.token, function () {
tokenService.setTokens(response.accessToken, response.refreshToken, function () {
cryptoService.setKey(key, function () {
cryptoService.setKeyHash(hashedPassword, function () {
if (response.profile) {
userService.setUserId(response.profile.id, function () {
userService.setEmail(response.profile.email, function () {
if (tokenService.isTwoFactorScheme()) {
deferred.resolve(response);
}
else {
userService.setUserId(tokenService.getUserId(), function () {
userService.setEmail(tokenService.getEmail(), function () {
chrome.runtime.sendMessage({ command: 'loggedIn' });
deferred.resolve(response);
});
});
}
else {
deferred.resolve(response);
}
});
});
});

View File

@ -8,29 +8,12 @@ function ApiService(tokenService) {
function initApiService() {
// 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) {
var self = this;
this.tokenService.getToken(function (token) {
$.ajax({
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),
contentType: 'application/json; charset=utf-8',
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
ApiService.prototype.getAccountRevisionDate = function (success, error) {
@ -51,7 +52,7 @@ function initApiService() {
this.tokenService.getToken(function (token) {
$.ajax({
type: 'GET',
url: self.baseUrl + '/accounts/revision-date?access_token=' + token,
url: self.baseUrl + '/accounts/revision-date?access_token2=' + token,
dataType: 'json',
success: function (response) {
success(response);
@ -68,7 +69,7 @@ function initApiService() {
this.tokenService.getToken(function (token) {
$.ajax({
type: 'GET',
url: self.baseUrl + '/accounts/profile?access_token=' + token,
url: self.baseUrl + '/accounts/profile?access_token2=' + token,
dataType: 'json',
success: function (response) {
success(new ProfileResponse(response));
@ -121,7 +122,7 @@ function initApiService() {
this.tokenService.getToken(function (token) {
$.ajax({
type: 'GET',
url: self.baseUrl + '/settings/domains?excluded=false&access_token=' + token,
url: self.baseUrl + '/settings/domains?excluded=false&access_token2=' + token,
dataType: 'json',
success: function (response) {
success(new DomainsResponse(response));
@ -140,7 +141,7 @@ function initApiService() {
this.tokenService.getToken(function (token) {
$.ajax({
type: 'GET',
url: self.baseUrl + '/sites/' + id + '?access_token=' + token,
url: self.baseUrl + '/sites/' + id + '?access_token2=' + token,
dataType: 'json',
success: function (response) {
success(new LoginResponse(response));
@ -157,7 +158,7 @@ function initApiService() {
this.tokenService.getToken(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/sites?access_token=' + token,
url: self.baseUrl + '/sites?access_token2=' + token,
data: JSON.stringify(loginRequest),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
@ -176,7 +177,7 @@ function initApiService() {
this.tokenService.getToken(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/sites/' + id + '?access_token=' + token,
url: self.baseUrl + '/sites/' + id + '?access_token2=' + token,
data: JSON.stringify(loginRequest),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
@ -197,7 +198,7 @@ function initApiService() {
this.tokenService.getToken(function (token) {
$.ajax({
type: 'GET',
url: self.baseUrl + '/folders/' + id + '?access_token=' + token,
url: self.baseUrl + '/folders/' + id + '?access_token2=' + token,
dataType: 'json',
success: function (response) {
success(new FolderResponse(response));
@ -214,7 +215,7 @@ function initApiService() {
this.tokenService.getToken(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/folders?access_token=' + token,
url: self.baseUrl + '/folders?access_token2=' + token,
data: JSON.stringify(folderRequest),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
@ -233,7 +234,7 @@ function initApiService() {
this.tokenService.getToken(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/folders/' + id + '?access_token=' + token,
url: self.baseUrl + '/folders/' + id + '?access_token2=' + token,
data: JSON.stringify(folderRequest),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
@ -254,7 +255,7 @@ function initApiService() {
this.tokenService.getToken(function (token) {
$.ajax({
type: 'GET',
url: self.baseUrl + '/ciphers/' + id + '?access_token=' + token,
url: self.baseUrl + '/ciphers/' + id + '?access_token2=' + token,
dataType: 'json',
success: function (response) {
success(new CipherResponse(response));
@ -271,7 +272,7 @@ function initApiService() {
this.tokenService.getToken(function (token) {
$.ajax({
type: 'GET',
url: self.baseUrl + '/ciphers?access_token=' + token,
url: self.baseUrl + '/ciphers?access_token2=' + token,
dataType: 'json',
success: function (response) {
var data = [];
@ -293,7 +294,7 @@ function initApiService() {
this.tokenService.getToken(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/ciphers/' + id + '/delete?access_token=' + token,
url: self.baseUrl + '/ciphers/' + id + '/delete?access_token2=' + token,
dataType: 'text',
success: function (response) {
success();

View File

@ -3,7 +3,22 @@
};
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) {
if (!callback || typeof callback !== 'function') {
@ -11,8 +26,9 @@ function initTokenService() {
}
_token = token;
_decodedToken = null;
chrome.storage.local.set({
'authBearer': token
'accessToken': token
}, function () {
callback();
});
@ -27,48 +43,88 @@ function initTokenService() {
return callback(_token);
}
chrome.storage.local.get('authBearer', function (obj) {
if (obj && obj.authBearer) {
_token = obj.authBearer;
chrome.storage.local.get('accessToken', function (obj) {
if (obj && obj.accessToken) {
_token = obj.accessToken;
}
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) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
_token = null;
chrome.storage.local.remove('authBearer', function () {
callback();
_token = _decodedToken = _refreshToken = null;
chrome.storage.local.remove('accessToken', function () {
chrome.storage.local.remove('refreshToken', function () {
callback();
});
});
};
// jwthelper methods
// ref https://github.com/auth0/angular-jwt/blob/master/src/angularJwt/services/jwt.js
TokenService.prototype.decodeToken = function (token) {
var parts = token.split('.');
TokenService.prototype.decodeToken = function () {
if (_decodedToken) {
return _decodedToken;
}
if (!_token) {
throw 'Token not found.';
}
var parts = _token.split('.');
if (parts.length !== 3) {
throw new Error('JWT must have 3 parts');
throw 'JWT must have 3 parts';
}
var decoded = urlBase64Decode(parts[1]);
if (!decoded) {
throw new Error('Cannot decode the token');
throw 'Cannot decode the token';
}
return JSON.parse(decoded);
};
TokenService.prototype.getTokenExpirationDate = function (token) {
var decoded = this.decodeToken(token);
TokenService.prototype.getTokenExpirationDate = function () {
var decoded = this.decodeToken();
if (typeof decoded.exp === "undefined") {
if (typeof decoded.exp === 'undefined') {
return null;
}
@ -78,8 +134,8 @@ function initTokenService() {
return d;
};
TokenService.prototype.isTokenExpired = function (token, offsetSeconds) {
var d = this.getTokenExpirationDate(token);
TokenService.prototype.isTokenExpired = function (offsetSeconds) {
var d = this.getTokenExpirationDate();
offsetSeconds = offsetSeconds || 0;
if (d === null) {
return false;
@ -89,6 +145,42 @@ function initTokenService() {
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) {
var output = str.replace(/-/g, '+').replace(/_/g, '/');
switch (output.length % 4) {