nebular
nebular copied to clipboard
How to refresh tokens in NbPasswordAuthStrategy
I would like to know how refreshing tokens with the NbPasswordAuthStrategy is supposed to be implemented on the server side. As there is no refresh-/access-token concept like in the NbOAuth2AuthStrategy, I have the impression that it is only possible to refresh a token using an already expired token. Is that correct? I read that refreshing an expired token has security implications.
Hi!
I also want to implement a refreshing token concept with NbPasswordAuthStrategy but I can't find how I can store and reach refresh token using NbAuthService. If you find the solution could you help me?
Thanks advanced, l.
below snippets should do the trick.
NbAuthModule.forRoot({
strategies: [
NbPasswordAuthStrategy.setup({
name: 'email',
...
token: {
class: NbAuthOAuth2JWTToken,
key: 'token'
}
})
]
...
{
provide: HTTP_INTERCEPTORS,
useClass: NbAuthJWTInterceptor,
multi: true
}
...
in server
function login(req, res) {
//login logic to be added
const dbUser =loggedInuser
res.json({
token: {
access_token: 'jwt access token here',
refresh_token: 'refreshtoken here'
});
}
function refreshToken(req, res) {
const reqAccessToken = req.body.token.access_token;
const reqRefreshToken = req.body.token.refresh_token;
const jwtPayload = undefined; // add logic for decode access token
const dbUser = undefined; // add logic for db user lookup using jwtPayload.user_id
// generate new access token
res.json({
token: {
access_token: 'jwt access token here',
refresh_token: 'same refreshtoken here'
});
}
this would be very helpful functionality
@sasikumardr I know this is an old post but I'm curious if this actually works for you? I tried this, but I don't believe the refreshToken strategy is being called from this.authService.isAuthenticatedOrRefresh() in the NbAuthJWTInterceptor interceptor.
I can successfully log in, but when it comes time to refresh my token it seems to loop at the interceptor, and I don't ever see my backend endpoint being hit.
@sasikumardr I know this is an old post but I'm curious if this actually works for you? I tried this, but I don't believe the refreshToken strategy is being called from this.authService.isAuthenticatedOrRefresh() in the NbAuthJWTInterceptor interceptor.
I can successfully log in, but when it comes time to refresh my token it seems to loop at the interceptor, and I don't ever see my backend endpoint being hit.
Just in case anyone else runs into this issue, I needed to have the endpoint for my refresh-token added to the NB_AUTH_TOKEN_INTERCEPTOR_FILTER
provider
I had one for my login endpoint, but realized I needed a second one for my refresh endpoint as well
provide: NB_AUTH_TOKEN_INTERCEPTOR_FILTER,
useValue: function (req: HttpRequest<any>) {
if (req.url === `${environment.apiUrl}/login`)
{
return true
}
if (req.url === `${environment.apiUrl}/refresh-token`)
{
return true
}
else {
return false
}
},
found this over at https://github.com/akveo/ngx-admin/issues/1375
I still have the same problem.
I tried to implemente refresh-token as @sasikumardr said, but still doesn't work.
If I use the NbAuthJWTToken the app just work fine, but when the token expires, the token-refresh endpoint, is called, issued a new one but it seems it is not stored anywhere.
If I try the NbAuthOAuth2JWTToken implementation, then I cannot even login. even with the code proposed by @dpcamp
EDIT: Debugging I see that AuthGuard has the authenticated parameter set to undefined. So login is working but somehow authguard, is not.
isAuthenticatedOrRefresh() { ... ... return of(token.isValid()); <---------------- token.token == token.payload
EDIT2: For the next one, just keep in mind that you have to return in your server a token object, i've just realised that was returning different thing and not exactly:
{
token: {
access_token: 'jwt access token here',
refresh_token: 'same refreshtoken here'
}
}
EDIT3: Still not working... I am in the same situation as with NbAuthJWTToken. The new token is not stored or used
EDIT4: Too many hours working on a row :) I can say that everything works perfectly
@AllTerrainDeveloper what was your final solution?
Sorry, I can't remember, but it was my fault.
My initial problem was that client was not storing the new token after refresh the token. I changed the NbAuthJWTToken with the OAuth2 one, it now made the client to look into an object called token, with the fields access_token and refresh_token. I was sending from my server side, two token objects, instead of on token object, with the two tokens inside.
But I barely remember, that I had changed the token key to token_access, instead of just token.
Loving so far this nebular framework
I'm trying to make own NbAuthJWTToken class (i add only RefreshToken), but in NbAuthTokenParceler methods wrap and unwrap i'm losing my extra properites.
Ok, i'm make a workaround. In my AuthStrategy (based on NbAuthStrategy) in methods createToken
and refreshToken
i'm store and load refresh token from localstorage.
Does anyone know how the refresh token strategy works for NbOAuth2AuthStrategy? I'm having some problems with it: #2442
@AllTerrainDeveloper Hello I am stuck on this exact problem, would you mind explaining how to use the NbOAuth2AuthStrategy to look into the token object? My token is returned from Django Rest Framework and consists of
{access: xxxx.yyyy.zzzz, refresh: aaaa.bbbb.cccc}
Hi, @victor-wyk Could you please explain how you solved this issue? I faced same problem and couldn't find answer.
It is very easy. For Django Rest Framework, we need to use djangorestframework-jwt library. This library will have two urls, token obtain and token refresh, instead of having both token refresh and obtain in the same url. This way you can provide the token format to Nebular's NbJWTAuthStrategy, instead of using OAuth2 which is more complex imho.
{ token: 'xxxx.yyyy.zzzz.' }
Look at my solution for refresh token https://github.com/akveo/nebular/issues/2639#issuecomment-1183414210
This is my solution with angular 15 and nebular auth 9 app.module.ts
const formSetting: any = {
redirectDelay: 0,
showMessages: {
success: true,
},
};
export function filterInterceptorRequest(req: HttpRequest<any>) {
return ['http://localhost:3001/auth/',
]
.some(url => req.url.includes(url));
}
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
NbSecurityModule.forRoot({
accessControl: {
guest: {
view: ['news', 'comments'],
},
user: {
parent: 'guest',
create: 'comments',
},
moderator: {
parent: 'user',
create: 'news',
remove: '*',
},
},
}),
NbAuthModule.forRoot({
strategies: [
NbPasswordAuthStrategy.setup({
name: 'email',
baseEndpoint: 'http://localhost:3001',
token: {
class: NbAuthOAuth2JWTToken,
getter: function (method: any, response: any) {
return {
access_token: response.body.access_token,
refresh_token: response.body.refresh_token,
};
},
},
refreshToken: {
endpoint: '/auth/refresh',
method: 'get',
requireValidToken: true,
redirect: {
success: null,
failure: null,
},
defaultErrors: ['Something went wrong, please try again.'],
defaultMessages: ['Your token has been successfully refreshed.'],
},
login: {
alwaysFail: false,
endpoint: '/auth/login',
method: 'post',
requireValidToken: true,
redirect: {
success: '/home',
failure: null,
},
defaultErrors: ['Email o contraseña incorrectass, inténtelo de nuevo.'],
defaultMessages: ['Has ingresado exitosamente.'],
},
register: {
endpoint: '/auth/sign-up',
method: 'post',
},
logout: {
endpoint: '/auth/logout',
method: 'post',
},
requestPass: {
endpoint: '/auth/request-pass',
method: 'post',
},
resetPass: {
endpoint: '/auth/reset-pass',
method: 'post',
},
}),
],
forms: {
login: formSetting,
register: formSetting,
requestPassword: formSetting,
resetPassword: formSetting,
logout: {
redirectDelay: 0,
},
},
}),
BrowserAnimationsModule,
NbThemeModule.forRoot({name: 'default'}),
NbLayoutModule,
NbEvaIconsModule,
],
providers: [AuthGuard,
{provide: HTTP_INTERCEPTORS, useClass: NbAuthJWTInterceptor, multi: true},
{provide: HTTP_INTERCEPTORS, useClass: BearerInterceptor, multi: true},
{provide: NB_AUTH_TOKEN_INTERCEPTOR_FILTER, useValue: filterInterceptorRequest},
{provide: NbRoleProvider, useClass: CustomRoleInterceptor},
],
bootstrap: [AppComponent]
})
export class AppModule {
}
Bearer Interceptor
@Injectable()
export class BearerInterceptor implements HttpInterceptor {
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (req.url.includes("/auth/refresh")) {
const token = JSON.parse(localStorage.getItem('auth_app_token') || '{}');
console.log(token);
const refreshToken = JSON.parse(token.value).refresh_token;
if (refreshToken) {
const cloned = req.clone({
headers: req.headers.set('Authorization', 'Bearer ' + refreshToken),
});
return next.handle(cloned);
} else {
return next.handle(req);
}
} else {
return next.handle(req);
}
}
}
Bearer interceptor is necesary if you use in backend header for send refreshToken, if you use post nebular Auth send payload for refresh Token.