Chetan Birajdar
Chetan Birajdar

Reputation: 471

How to refresh token in Angular 6 and web api using jwt?

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

http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/

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

Answers (1)

The Unculled Badger
The Unculled Badger

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..

enter image description here

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

Related Questions