Reputation: 5255
I am using following package to implement Azure AD auth in an Angular application:
https://www.npmjs.com/package/adal-angular4
After 10-20 minutes the token expires. There is another thread where the discuss something similar, but I simply cant find a valid solution. To be honest when reading the post I am not even sure if it is possible due to the nature of implicit flow. All pointers are welcome.
Angular 2 ADAL token refresh, for implicit flow (using "adal-angular4")
I have my authredirect page where Azure redirects to:
ngOnInit() {
this.adal.handleWindowCallback();
sessionStorage.setItem("adal.username",this.adal.userInfo.userName);
this.loginService.userLoggedIn(this.adal.userInfo.userName);
console.log("in authredirect. setting user in storage:"+this.adal.userInfo.userName);
var url = sessionStorage.getItem("adal.return.url");
console.log(url);
setTimeout(() => {
this._zone.run(
() => this.router.navigate([url])
);
}, 500);
}
In my app.module.ts I have following:
providers: [
AdalService,
AuthenticationGuard,
{ provide: HTTP_INTERCEPTORS, useClass: AuthenticationInterceptor, multi: true }
In my app.component in the constructor:
this.adalService.init(environment.adalConfig);
My authentication guard:
export class AuthenticationGuard implements CanActivate {
constructor(private adal: AdalService, private route: ActivatedRoute) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
if (this.adal.userInfo.authenticated) {
return true;
}
console.log("Adal Login");
var url = state.url;
console.log(url);
sessionStorage.setItem("adal.return.url",url);
this.adal.login();
console.log('AuthGuard'+this.adal);
return false;
}
}
And finally my app.routing.ts:
{
path: 'chartering',
loadChildren: './views/chartering/chartering.module#CharteringModule',
canActivate: [AuthenticationGuard]
},
Upvotes: 1
Views: 2042
Reputation: 594
You are in luck I have been through this exact same problem before and it took me weeks to resolve it. To acquire a access token and a refresh token you need to use Microsoft's MSAL library instead of ADAL. There is absolutely no way to refresh your token without using MSAL. Microsoft's MSAL library is useless though. I wrote a simplified version from scratch specifically for my Angular application. I uploaded it to github for you. MsalService.ts. Hopefully you can make use of it.
Basically whenever you need an authentication token use the msalService.acquireToken
function. If no token is returned from that function the user should be logged out. Use msalService.login
and msalService.logout
to well, log the user in or out.
Due to CORS limitations you cannot request an access token or refresh token from the client side of your application. I created endpoints on my server to make the requests.
[HttpPost, Route("AccessToken")]
public async Task<HttpResponseMessage> GetAccessToken(AccessTokenRequest accessTokenRequest)
{
HttpClient httpClient = new HttpClient();
var content = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("client_id", "<insert_your_client_id>"),
new KeyValuePair<string, string>("code", accessTokenRequest.AuthorizationCode),
new KeyValuePair<string, string>("redirect_uri", RemoveHashFromUri(accessTokenRequest.RedirectUri)),
new KeyValuePair<string, string>("client_secret", "<insert_your_client_secret>"),
new KeyValuePair<string, string>("scope", accessTokenRequest.Scope)
};
var response = await httpClient.PostAsync($"https://login.microsoftonline.com/{your_tenant_id}/oauth2/v2.0/token", new FormUrlEncodedContent(content));
return response;
}
[HttpPost, Route("RefreshToken")]
public async Task<HttpResponseMessage> RefreshToken(AccessTokenRequest accessTokenRequest)
{
HttpClient httpClient = new HttpClient();
var content = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "refresh_token"),
new KeyValuePair<string, string>("client_id", "<insert_your_client_id>"),
new KeyValuePair<string, string>("refresh_token", accessTokenRequest.AuthorizationCode),
new KeyValuePair<string, string>("client_secret", "<insert_your_client_secret>"),
};
var response = await httpClient.PostAsync($"https://login.microsoftonline.com/{your_tenant_id}/oauth2/v2.0/token", new FormUrlEncodedContent(content));
return response;
}
private string RemoveHashFromUri(string redirectUri)
{
if (string.IsNullOrWhiteSpace(redirectUri))
{
return null;
}
if (redirectUri.Contains("#"))
{
return redirectUri.Substring(0, redirectUri.IndexOf("#"));
}
return redirectUri;
}
public class AccessTokenRequest
{
public string AuthorizationCode { get; set; }
public string RedirectUri { get; set; }
public string Scope { get; set; }
}
In the constructor or ngOnInit() of your app component you need this code to process the callback when the user actually logins in to Microsoft. With the token returned from Microsoft in the url hash you can then get an access token.
let authorizationCallback: IAuthorizationCallback = this.msalService.processAuthorizationCallback();
if (authorizationCallback.code) {
// call out to your server for an access token
this.resourceFactory.create("Auth/AccessToken").post({
authorizationCode: authorizationCallback.code,
redirectUri: this.msalService.getRedirectUrl(),
scope: this.msalService.getScopes()
}).then((json: any) => {
try {
this.msalService.cacheAuthentication(json);
} catch (error) {
// handle error
}
}).catch((error: any) => {
// handle error
});
} else if (authorizationCallback.error) {
// handle error. Check authorizationCallback.error and authorizationCallback.error_description
}
Let me know if you have any issues.
Upvotes: 1