mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-28 17:27:50 +01:00
convert to identityserver auth
This commit is contained in:
parent
f84bfcb19a
commit
6bb6c7074b
@ -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) {
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user