Reputation: 471
I have used jwt tokens for my angular 6 project with Web API. I can login to my session by generating token , but when it expires lets say after 10 min. I want to show a popup, which would say, your session is expired please click below to refresh you session. This would generate a new token, which can be used to access my portal.
I have tried following code , using some links below
https://steemit.com/utopian-io/@babelek/how-to-manage-with-refresh-token-in-asp-net-core-2-0-web-api
Refresh token (JWT) in interceptor Angular 6
In my auth Interceptor file in angular 6. file I have added code as below
intercept(req: HttpRequest<any>, next: HttpHandler) {
const token = sessionStorage.getItem('token');
const authReq = req.clone({ setHeaders: { Authorization: 'bearer ' +
token } });
return next.handle(authReq).do((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// if the token is valid
}
}, (err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.status === 401) {
const refreshToken =
sessionStorage.getItem('refreshToken');
this.loginService.refreshToken([refreshToken]).subscribe((res: any) => {
const tokenInfo =
this.loginService.getDecodedAccessToken(res.access_token);
const loggedUser = tokenInfo.userName;
this.loginService.setToken(res.access_token,
loggedUser, res.refresh_token);
sessionStorage.setItem('userName', loggedUser);
}, error => {
});
this.router.navigateByUrl('/login');
this.authService.collectFailedRequest(authReq);
}
}
});
}
In my login service file I have added below code
public getToken(parameter: string[]): Observable<any> {
const data = new HttpParams()
.set('username', parameter[0])
.set('password', parameter[1])
.set('grant_type', 'password');
const url = this.API_URL + '/api/confirm_login';
return this._httpClient.post(url, data.toString()).pipe(
map(x => x),
catchError((error: any) => {
let errorMessage: any;
if (error.error && error.error.error_description) {
errorMessage = error.error.error_description;
}
throw errorMessage;
})
);
}
public refreshToken(parameter: string[]): Observable<any> {
const data = new HttpParams()
.set('refresh_token', parameter[0])
.set('grant_type', 'refresh_token');
const url = this.API_URL + '/api/refresh_token';
return this._httpClient.post(url, data.toString()).pipe(map(x => x),
catchError((error: any) => {
let errorMessage: any;
if (error.error && error.error.error_description) {
errorMessage = error.error.error_description;
}
throw errorMessage;
})
);
}
public setToken(token: any, getLoggedUser: any, refresh_token: any) {
sessionStorage.setItem('token', token);
sessionStorage.setItem('userName', getLoggedUser);
sessionStorage.setItem('refreshToken', refresh_token);
}
getDecodedAccessToken(token: string): any {
try {
return jwt_decode(token);
} catch (Error) {
return null;
}
}
In my authservice file I have added
authenticated(): boolean {
const token = sessionStorage.getItem('token');
if (!token) {
return null;
}
return !this.jwtHelper.isTokenExpired(token);
}
In my startup.cs file I have added
public void ConfigureOAuth(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
OAuthAuthorizationServerOptions OAuthServerOptions = new
OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/confirm_login"),
AccessTokenExpireTimeSpan = TimeSpan.FromMilliseconds(30000),
Provider = new ldAuthorizationServerProvider(),
AccessTokenFormat = new
ldTokenFormat(ConfigurationManager.AppSettings["as:AngularHostURL"]),
RefreshTokenProvider = new RefreshTokenProvider(),
AuthorizeEndpointPath = new PathString("/api/refresh_token"),
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
This is my refreshtokenprovider
public class RefreshTokenProvider : IAuthenticationTokenProvider
{
private static ConcurrentDictionary<string, AuthenticationTicket>
_refreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>();
public async Task CreateAsync(AuthenticationTokenCreateContext
context)
{
var guid = Guid.NewGuid().ToString();
// copy all properties and set the desired lifetime of refresh
token
var refreshTokenProperties = new
AuthenticationProperties(context.Ticket.Properties.Dictionary)
{
IssuedUtc = context.Ticket.Properties.IssuedUtc,
ExpiresUtc =
DateTime.UtcNow.AddMilliseconds(30000)//DateTime.UtcNow.AddYears(1)
};
var refreshTokenTicket = new
AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties);
_refreshTokens.TryAdd(guid, refreshTokenTicket);
// consider storing only the hash of the handle
context.SetToken(guid);
}
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
Guid token;
if (Guid.TryParse(context.Token, out token))
{
AuthenticationTicket ticket;
if (_refreshTokens.TryRemove(token, out ticket))
{
context.SetTicket(ticket);
}
}
}
Here my code is not reaching to GrantRefreshToken Method. I am not sure what I am Missing.
Any help is appreciated
Upvotes: 0
Views: 902
Reputation: 760
In the example I have the shared token endpoint for logging in and refreshing the token is /Token. I have exactly the same code as you with the exception of the token endpoint. When I make an postman request to it, I hit the GrantRefreshToken code..
Alternatively the angular code for this is
refreshToken(): Observable<any> {
let headers = new Headers({ 'Content-type': 'application/x-www-form-urlencoded' });
headers.append('Content-Type', 'application/json');
headers.append('No-Auth', 'True');
var refreshToken = <the guid in the refresh_token local storage>
let body = 'grant_type=refresh_token&refresh_token=' + refreshToken;
return this._http.post(<some url> + '/Token', body, {headers:headers});
}
Upvotes: 1