scripts/core/auth/auth.ts
import {gettext} from 'core/utils';
import {AuthoringWorkspaceService} from 'apps/authoring/authoring/services/AuthoringWorkspaceService';
import {appConfig} from 'appConfig';
import ng from 'core/services/ng';
export const SESSION_EVENTS = {
LOGIN: 'login',
LOGOUT: 'logout',
IDENTITY_LOADED: 'identity_loaded',
};
const {fetch: originalFetch} = window;
window.fetch = (...args) => {
const [resource, config] = args;
/**
* Prevent requests not coming from our app get intercepted.
*/
if (resource.toString().includes(appConfig.server.url) === false) {
return originalFetch(resource, config);
}
return originalFetch(resource, config)
.then((resp) => {
if (resp.status === 401) {
const session = ng.get('session');
session.expire();
return session.getIdentity().then(() => {
return originalFetch(resource, {
...config,
headers: {
'Authorization': session.token,
},
});
});
}
return resp;
});
};
/**
* Expire session on 401 server response
*/
AuthExpiredInterceptor.$inject = ['session', '$q', '$injector', '$browser', 'lodash'];
function AuthExpiredInterceptor(session, $q, $injector, $browser, _) {
function handleAuthExpired(response) {
$browser.$$completeOutstandingRequest(angular.noop);
session.expire();
return session.getIdentity().then(() => {
const $http = $injector.get('$http');
$browser.$$incOutstandingRequestCount();
$http.defaults.headers.common.Authorization = session.token;
response.config.headers.Authorization = session.token;
return $injector.get('request').resend(response.config);
});
}
return {
response: function(response) {
if (_.startsWith(response.config.url, appConfig.server.url) && response.status === 401) {
return handleAuthExpired(response);
}
return response;
},
responseError: function(response) {
if (_.startsWith(response.config.url, appConfig.server.url) && response.status === 401) {
if (!((response.data || {})._issues || {}).credentials) {
return handleAuthExpired(response);
}
}
return $q.reject(response);
},
};
}
angular.module('superdesk.core.auth.interceptor', ['superdesk.core.api', 'superdesk.core.auth.session'])
.service('AuthExpiredInterceptor', AuthExpiredInterceptor);
ResetPassworController.$inject = ['$scope', '$location', 'api', 'notify'];
function ResetPassworController($scope, $location, api, notify) {
$scope.isSending = false;
$scope.isReseting = false;
var resetForm = function() {
$scope.email = '';
$scope.token = '';
$scope.password = '';
$scope.passwordConfirm = '';
};
$scope.sendToken = function() {
$scope.sendTokenError = null;
api.resetPassword.create({email: $scope.email})
.then((result) => {
notify.success(gettext('Link sent. Please check your email inbox.'));
$scope.flowStep = 2;
}, (rejection) => {
$scope.sendTokenError = rejection.status;
});
resetForm();
};
$scope.resetPassword = function() {
$scope.setPasswordError = null;
api.resetPassword.create({token: $scope.token, password: $scope.password})
.then((result) => {
notify.success(gettext('Password was changed. You can login using your new password.'));
$location.path('/').search({});
}, (rejection) => {
$scope.setPasswordError = rejection.status;
});
resetForm();
};
resetForm();
var query = $location.search();
if (query.token) {
api.resetPassword.create({token: query.token})
.then((result) => {
$scope.token = query.token;
$scope.flowStep = 3;
}, (rejection) => {
$scope.setPasswordError = rejection.status;
$scope.flowStep = 1;
});
} else {
$scope.flowStep = 1;
}
}
/**
* @ngdoc controller
* @module superdesk.core.auth
* @name SecureLoginController
* @description this controller handles XMPP auth (aka secure login), it create
* transaction ID (which will be sent to XMPP client) and redirect page on success.
*/
SecureLoginController.$inject = ['$scope', 'auth', '$route', '$window'];
function SecureLoginController(scope, auth, $route, $window) {
var random = Math.floor(Math.random() * 10000 + 1);
scope.transactionId = random.toString();
scope.authenticateXMPP = function() {
scope.isLoading = true;
scope.loginError = null;
auth.loginXMPP(scope.jid || '', scope.transactionId || '')
.then(() => {
scope.isLoading = false;
$window.location.replace('/'); // reset page for new user
}, (rejection) => {
scope.isLoading = false;
scope.loginError = rejection.status;
});
};
}
angular.module('superdesk.core.auth.session', [])
.constant('SESSION_EVENTS', SESSION_EVENTS);
/**
* @ngdoc module
* @module superdesk.core.auth
* @name superdesk.core.auth
* @packageName superdesk.core
* @description Superdesk core authentication and session related functionalities.
*/
export default angular.module('superdesk.core.auth', [
'superdesk.core.features',
'superdesk.core.activity',
'superdesk.core.auth.session',
'superdesk.core.services.asset',
'superdesk.config',
'superdesk.core.auth.auth',
'superdesk.core.auth.basic',
'superdesk.core.auth.login',
'superdesk.core.auth.keycloak',
'superdesk.core.auth.interceptor',
])
.config(['$httpProvider', 'superdeskProvider', 'assetProvider', function($httpProvider, superdesk, asset) {
$httpProvider.interceptors.push('AuthExpiredInterceptor');
superdesk
.activity('/reset-password/', {
controller: ResetPassworController,
templateUrl: asset.templateUrl('core/auth/reset-password.html'),
auth: false,
});
superdesk
.activity('/secure-login/', {
controller: SecureLoginController,
templateUrl: asset.templateUrl('core/auth/secure-login.html'),
auth: false,
});
}])
.config(['apiProvider', function(apiProvider) {
apiProvider.api('resetPassword', {
type: 'http',
backend: {
rel: 'reset_user_password',
},
});
apiProvider.api('auth', {
type: 'http',
backend: {
rel: 'auth',
},
});
}])
// watch session token, identity
.run(['$rootScope', '$http', '$window', 'session', 'api', 'superdeskFlags', 'authoringWorkspace',
'modal',
function(
$rootScope,
$http,
$window,
session,
api,
superdeskFlags,
authoringWorkspace: AuthoringWorkspaceService,
modal,
) {
$rootScope.logout = function() {
var canLogout = true;
if (superdeskFlags.flags.authoring) {
var item = authoringWorkspace.getItem();
if (item && item._autosaved) {
canLogout = false;
modal.confirm(gettext('There are some unsaved changes. Please save them before signing out.'),
gettext('Warning'), gettext('OK'), '');
}
}
if (canLogout) {
api.auth.getById(session.sessionId).then((sessionData) => {
api.auth
.remove(sessionData)
.finally(() => {
$rootScope.$broadcast(SESSION_EVENTS.LOGOUT);
localStorage.clear();
$window.location.replace('/'); // reset page for new user
});
});
}
};
// populate current user
$rootScope.$watch(function watchSessionIdentity() {
return session.identity;
}, (identity) => {
$rootScope.currentUser = session.identity;
$rootScope.$broadcast(SESSION_EVENTS.IDENTITY_LOADED);
});
// set auth header
$rootScope.$watch(function watchSessionToken() {
return session.token;
}, (token) => {
if (token) {
$http.defaults.headers.common.Authorization = token;
$rootScope.sessionId = session.sessionId;
} else {
delete $http.defaults.headers.common.Authorization;
$rootScope.sessionId = null;
}
});
}]);