Currently I am using ADAL.NET in our website which follows MVC architecture. I use session cache for storing the user tokens.
Can I used ADAL.JS instead of ADAL.NET and store the user token in the browser/client to avoid using the session cache?
答案 0 :(得分:2)
Yes you can. We are currently doing this using the adal js library. We're not using angularjs, so we are using the underlying AuthenticationContext as provided, but have an adapted angularjs client layer for our MVC app
https://github.com/AzureAD/azure-activedirectory-library-for-js/blob/master/README.md
Here is some sample code:
/// <reference path="../lib/adal/index.js" />
'use strict';
import AuthenticationContext from '../lib/adal/lib/adal';
import unauthorized from './unauthorized';
// endpoints is a map of base urls to the app URI in the AAD Application configuration - e.g. {'https://host/services/api/': 'https://appuri.contoso.com' }
class AuthenticationService {
constructor(jQuery, webClientId, endpoints, domainHint) {
var userInfo = { isAuthenticated: false, userName: '', loginError: '', profile: '' };
var updateDataFromCache = function (adal, resource) {
// only cache lookup here to not interrupt with events
var token = adal.getCachedToken(resource);
userInfo.isAuthenticated = token != null && token.length > 0;
var user = adal.getCachedUser() || { userName: '' };
userInfo.userName = user.userName;
userInfo.profile = user.profile;
userInfo.loginError = adal.getLoginError();
};
var locationChangeHandler = function (adal) {
var hash = window.location.hash;
if (adal.isCallback(hash)) {
// callback can come from login or iframe request
var requestInfo = adal.getRequestInfo(hash);
adal.saveTokenFromHash(requestInfo);
window.location.hash = '';
if (requestInfo.requestType !== adal.REQUEST_TYPE.LOGIN) {
adal.callback = window.parent.AuthenticationContext().callback;
if (requestInfo.requestType === adal.REQUEST_TYPE.RENEW_TOKEN) {
adal.callback = window.parent.callBackMappedToRenewStates[requestInfo.stateResponse];
}
}
// Return to callback if it is send from iframe
if (requestInfo.stateMatch) {
if (typeof adal.callback === 'function') {
// Call within the same context without full page redirect keeps the callback
if (requestInfo.requestType === adal.REQUEST_TYPE.RENEW_TOKEN) {
// Idtoken or Accestoken can be renewed
if (requestInfo.parameters['access_token']) {
adal.callback(adal._getItem(adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION), requestInfo.parameters['access_token']);
} else if (requestInfo.parameters['id_token']) {
adal.callback(adal._getItem(adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION), requestInfo.parameters['id_token']);
}else {
adal.callback(adal._getItem(adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION), requestInfo.parameters.error);
}
}
} else {
// normal full login redirect happened on the page
updateDataFromCache(adal, adal.config.loginResource);
if (userInfo.userName) {
//IDtoken is added as token for the app
adal._logstatus('adal:loginSuccess');
} else {
adal._logstatus('adal:loginFailure', adal._getItem(adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION));
}
}
}
} else {
// No callback. App resumes after closing or moving to new page.
// Check token and username
updateDataFromCache(adal, adal.config.loginResource);
if (!adal._renewActive && !userInfo.isAuthenticated && userInfo.userName) {
if (!adal._getItem(adal.CONSTANTS.STORAGE.FAILED_RENEW)) {
// Idtoken is expired or not present
adal.acquireToken(adal.config.loginResource, function (error, tokenOut) {
if (error) {
adal._logstatus('adal:loginFailure', 'auto renew failure');
} else {
if (tokenOut) {
userInfo.isAuthenticated = true;
}
}
});
}
}
}
updateDataFromCache(adal, adal.config.loginResource);
return userInfo.isAuthenticated;
};
var loginHandler = function (adal) {
adal._logstatus('Login event for:' + window.location);
if (adal.config && adal.config.localLoginUrl) {
window.location.pathname = adal.config.localLoginUrl;
} else {
// directly start login flow
adal._saveItem(adal.CONSTANTS.STORAGE.START_PAGE, window.location.pathname);
adal._logstatus('Start login at:' + window.location.href);
adal.login();
}
};
var createAdal = function() {
// make sure adal is singleton - get from window/store on window
if (window.parent.AuthenticationContext) return window.parent.AuthenticationContext();
var currentUrl = window.location.origin;
var adal = AuthenticationContext.inject({
clientId: clientId,
cacheLocation: 'localStorage',
endpoints: endpoints,
// make sure to exclude any hash (#) since MS validates on a redirectUri match and will fail even if there is an empty trailing #1
redirectUri: window.location.protocol
+ "//" + window.location.hostname
+ (window.location.port === 80 || window.location.port === 443 ? '' : ':' + (window.location.port))
+ window.location.pathname
});
var promptUser = adal.promptUser;
adal.promptUser = function() {
if (arguments[0].indexOf('domain_hint') == -1) arguments[0] += '&domain_hint=' + domainHint;
if (adal.useFormsAuth) arguments[0] += '&prompt=login';
return promptUser.apply(adal, arguments);
}
// hack to simulate "global scope" of ADAL when not imported as module - this is necessary because of how ADAL deals with iframes
window.parent.AuthenticationContext = adal.__proto__.constructor;
return adal;
}
var acquireToken = function(adal, resource) {
return new Promise(function(resolve, reject) {
// automated token request call
adal.acquireToken(resource, function(error, tokenOut) {
if (error) {
adal._logstatus('err :' + error);
adal.clearCacheForResource(resource);
}
if (typeof error === 'string' && error.indexOf('AADSTS65001') == 0) { // interaction required
var urlNavigate = adal._getNavigateUrl('token', resource) + '&login_hint=' + encodeURIComponent(adal._user.userName);
window.location.href = urlNavigate;
return;
} else if (error) {
reject(error);
} else {
resolve(tokenOut);
}
});
});
};
var configureJQuery = function(adal) {
if (window.parent.hasConfiguredJQueryAuth) {
return;
}
window.parent.hasConfiguredJQueryAuth = true;
jQuery(window).on('hashchange', function() { locationChangeHandler(adal); });
// intercept ajax method as there is no way of deferring a call from beforeSend
var ajax = jQuery.ajax;
adal._logstatus('Performing jQuery configuration for auth');
jQuery.ajax = function() {
var deferred = jQuery.Deferred();
function invoke(call) {
call
.fail(function() { deferred.reject.apply(deferred, arguments); })
.done(function() { deferred.resolve.apply(deferred, arguments); });
}
var args = arguments;
var options = args[0];
if (!options) {
return ajax.apply(jQuery, args);
}
if (!options.headers) options.headers = {};
// get resource
var resource = adal.getResourceForEndpoint(options.url);
if (resource === adal.config.loginResource) {
// try again for absolute url
resource = adal.getResourceForEndpoint(document.location.origin + options.url);
}
if (resource === adal.config.loginResource) {
// no auth required
invoke(ajax.apply(jQuery, args));
return deferred.promise();
}
var tokenStored = adal.getCachedToken(resource);
if (tokenStored) {
// got from cache
options.headers['Authorization'] = 'Bearer ' + tokenStored;
invoke(ajax.apply(jQuery, args));
return deferred.promise();
}
// new token required
acquireToken(adal, resource).then(function fulfilled(tokenOut) {
options.headers['Authorization'] = 'Bearer ' + tokenOut;
invoke(ajax.apply(jQuery, args));
}, function rejected(error) {
deferred.reject(error);
});
return deferred.promise();
};
jQuery(document).ajaxError(function(event, jqxhr, options) {
if (jqxhr.status === 401) {
var resource = adal.getResourceForEndpoint(options.url);
if (resource === adal.config.loginResource) {
resource = adal.getResourceForEndpoint(document.location.origin + options.url);
}
adal.clearCacheForResource(resource);
unauthorized.show();
}
});
};
// set up adal and jquery
var adal = createAdal();
configureJQuery(adal);
this.userInfo = userInfo;
this.config = adal.config;
this.login = function() {
adal.login();
};
this.loginInProgress = function() {
return adal.loginInProgress();
};
this.getCachedToken = function(resource) {
return adal.getCachedToken(resource);
};
this.acquireToken = function(resource) { return acquireToken(adal, resource); };
this.getUser = function() {
return new Promise((resolve, reject) => {
adal.getUser(function(error, user) {
if (error) {
adal._logstatus('err :' + error);
reject(error);
} else {
resolve(user);
}
});
});
};
this.getResourceForEndpoint = function(endpoint) {
return adal.getResourceForEndpoint(endpoint);
};
this.clearCache = function() {
adal.clearCache();
};
this.clearCacheForResource = function(resource) {
adal.clearCacheForResource(resource);
};
this.loginAsADifferentUser = function() {
adal.useFormsAuth = true;
this.clearCache();
this.login();
}
this.init = function() {
locationChangeHandler(adal);
if (userInfo.isAuthenticated) {
return Promise.resolve();
}
loginHandler(adal);
return new Promise(function() {}); // wait for redirect
};
}
}
export default AuthenticationService;
Import AuthenticationService and call on load or when you want to authenticate
import AuthenticationService from './auth'
new AuthenticationService($)init.then(() => {
if (!authService.userInfo.isAuthenticated) {
if (!authService.loginInProgress()) {
unauthorized.show();
}
} else {
// succcess and userInfo available at authService.userInfo
});
From entry point handle callbacks in iframes differently:
$(document).ready(() => {
if (window === window.parent) {
// do regular init stuff
} else {
// callback from iframe for auth - just init auth so callbacks are processed
authService.init();
}
});