Solución:
Para resolver este mismo problema, me apoyé en ADAL JS. He hecho un Vue + Vue-Router aplicación de muestra disponible aquí, pero incluiré las piezas importantes a continuación.
En su package.json:
"dependencies": {
"adal-angular": "^1.0.15",
"vue": "^2.5.2",
"vue-router": "^3.0.1"
},
Un módulo contenedor básico para la biblioteca ADAL JS:
import AuthenticationContext from 'adal-angular/lib/adal.js'
const config = {
tenant: 'your aad tenant',
clientId: 'your aad application client id',
redirectUri: 'base uri for this application',
cacheLocation: 'localStorage'
};
export default {
authenticationContext: null,
/**
* @return {Promise}
*/
initialize() {
this.authenticationContext = new AuthenticationContext(config);
return new Promise((resolve, reject) => {
if (this.authenticationContext.isCallback(window.location.hash) || window.self !== window.top) {
// redirect to the location specified in the url params.
this.authenticationContext.handleWindowCallback();
}
else {
// try pull the user out of local storage
let user = this.authenticationContext.getCachedUser();
if (user) {
resolve();
}
else {
// no user at all - go sign in.
this.signIn();
}
}
});
},
/**
* @return {Promise.<String>} A promise that resolves to an ADAL token for resource access
*/
acquireToken() {
return new Promise((resolve, reject) => {
this.authenticationContext.acquireToken('<azure active directory resource id>', (error, token) => {
if (error || !token) {
return reject(error);
} else {
return resolve(token);
}
});
});
},
/**
* Issue an interactive authentication request for the current user and the api resource.
*/
acquireTokenRedirect() {
this.authenticationContext.acquireTokenRedirect('<azure active directory resource id>');
},
/**
* @return {Boolean} Indicates if there is a valid, non-expired access token present in localStorage.
*/
isAuthenticated() {
// getCachedToken will only return a valid, non-expired token.
if (this.authenticationContext.getCachedToken(config.clientId)) { return true; }
return false;
},
/**
* @return An ADAL user profile object.
*/
getUserProfile() {
return this.authenticationContext.getCachedUser().profile;
},
signIn() {
this.authenticationContext.login();
},
signOut() {
this.authenticationContext.logOut();
}
}
En el punto de entrada de la aplicación (main.js si usó vue-cli):
import Vue from 'vue'
import App from './App'
import router from './router'
import authentication from './authentication'
// Init adal authentication - then create Vue app.
authentication.initialize().then(_ => {
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
});
});
Para la configuración de su enrutador Vue:
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import authentication from '../authentication'
Vue.use(Router)
const router = new Router({
mode: 'history',
routes: [
{
path: "https://newbedev.com/",
name: 'HelloWorld',
component: HelloWorld,
meta: {
requiresAuthentication: true
}
}
]
})
// Global route guard
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuthentication)) {
// this route requires auth, check if logged in
if (authentication.isAuthenticated()) {
// only proceed if authenticated.
next();
} else {
authentication.signIn();
}
} else {
next();
}
});
export default router;
En sus componentes de Vue:
import authentication from './authentication'
...
computed: {
isAuthenticated() {
return authentication.isAuthenticated();
}
},
methods: {
logOut() {
authentication.signOut();
}
}
Agregar token de acceso para solicitar encabezados
El siguiente es un ejemplo de un interceptor http de recursos vue, pero cualquier método servirá.
Vue.http.interceptors.push(function (request, next) {
auth.acquireToken().then(token => {
// Set default request headers for every request
request.headers.set('Content-Type', 'application/json');
request.headers.set('Ocp-Apim-Subscription-Key', 'api key');
request.headers.set('Authorization', 'Bearer ' + token)
// continue to next interceptor
next();
});
});
Con suerte, esto le ahorrará a alguien un poco de tiempo 🙂
Descargo de responsabilidad: Soy el autor de este complemento.
Utilice vue-adal a través de npm:
npm install vue-adal
Uso básico
import Adal from 'vue-adal'
Vue.use(Adal, {
// This config gets passed along to Adal, so all settings available to adal can be used here.
config: {
// 'common' (multi-tenant gateway) or Azure AD Tenant ID
tenant: '<guid>',
// Application ID
clientId: '<guid>',
// Host URI
redirectUri: '<host addr>',
cacheLocation: 'localStorage'
},
// Set this to true for authentication on startup
requireAuthOnInitialize: true,
// Pass a vue-router object in to add route hooks with authentication and role checking
router: router
})
```
importante: asegúrese de configurar el modo en su enrutador en ‘historial’ para que no use hashes. Esto tendrá implicaciones en el lado del servidor.
new Router({
mode: 'history', // Required for Adal library
... // Rest of router init
})
Hay más instrucciones de uso en npm e instrucciones + una muestra en github
No estoy seguro de que haya una biblioteca para ayudar con la seguridad de las aplicaciones de Vue. Sin embargo, podemos aprovechar fácilmente Adal.js para la autenticación.
Escribí una demostración simple para su referencia:
Index.html:
<html>
<head>
<script src="https://unpkg.com/vue"></script>
<script src="node_modulesadal-angularlibadal.js"></script>
<script src="config.js"></script>
<script>
var authContext = new AuthenticationContext(config);
function login() {
authContext.login();
}
function init(configOptions) {
if (configOptions) {
// redirect and logout_redirect are set to current location by default
var existingHash = window.location.hash;
var pathDefault = window.location.href;
if (existingHash) {
pathDefault = pathDefault.replace(existingHash, "");
}
configOptions.redirectUri = configOptions.redirectUri || pathDefault;
configOptions.postLogoutRedirectUri =
configOptions.postLogoutRedirectUri || pathDefault;
// create instance with given config
} else {
throw new Error("You must set configOptions, when calling init");
}
// loginresource is used to set authenticated status
updateDataFromCache(authContext.config.loginResource);
}
var _oauthData = {
isAuthenticated: false,
userName: "",
loginError: "",
profile: ""
};
var updateDataFromCache = function(resource) {
// only cache lookup here to not interrupt with events
var token = authContext.getCachedToken(resource);
_oauthData.isAuthenticated = token !== null && token.length > 0;
var user = authContext.getCachedUser() || { userName: "" };
_oauthData.userName = user.userName;
_oauthData.profile = user.profile;
_oauthData.loginError = authContext.getLoginError();
};
function saveTokenFromHash() {
var hash = window.location.hash;
var requestInfo = authContext.getRequestInfo(hash);
if (authContext.isCallback(hash)) {
// callback can come from login or iframe request
var requestInfo = authContext.getRequestInfo(hash);
authContext.saveTokenFromHash(requestInfo);
window.location.hash = "";
if (requestInfo.requestType !== authContext.REQUEST_TYPE.LOGIN) {
authContext.callback = window.parent.AuthenticationContext().callback;
}
}
}
function isAuthenticate() {
return _oauthData.isAuthenticated;
}
saveTokenFromHash();
init(config);
</script>
</head>
<body>
<div>
<p v-if="_oauthData.isAuthenticated">Hello {{ oauthData.userName }}</p>
<button onclick="login()" v-else>Login</button>
</div>
<script>
var app = new Vue({
el: "#app",
data: {
oauthData: _oauthData
}
});
</script>
</body>
</html>
config.js:
var config = {
tenant: 'xxx.onmicrosoft.com',
clientId: '',
redirectUri: '',
cacheLocation: 'localStorage'
};