admin管理员组

文章数量:1414605

I have deployed my application to UAT but I am not able to run my application in mobile because it directly goes to access-denied(401) page. I think it is due to a access token issue.

I have mainly 2 interceptors to handle my application. 1.Error interceptor - which handles when there is any route error or unauthorized error. 2. jwt - assign token which internally calls the authentication-service to get token.

Below is my service file where I am trying to get access token in session or local or from the window event by sending a postMessage that i am ready to receive the token.

authentication-service.ts

public getToken() {
    let accessToken = null;
     const auth = JSON.parse(sessionStorage.getItem('auth'));
    if (auth) {
        accessToken = auth.access_token;
    } elseif (accessToken == null || accessToken === undefined) {
        accessToken = localStorage.getItem('access_token');
    }

 window.addEventListener('message', function(event){
      // this event should have all the necessary tokens
 }, false);

 // once my page is loaded to indicate that I am ready to receive the message from server side.
  parent.postMessage({ askForToken:"true"}, "*");

  return accessToken;
}

I am sending a parent.postMessage to make the window.addEventListener to retrive the data but the event is not sending the token as expected.

I am doing all these above code implementation in authentication.service.ts, I am not sure if it is right way to do it.

Can anyone please suggest me the right way to implement this code and receive token suitably?

Please correct me as I am trying deployment with tokens for the first time.

I have deployed my application to UAT but I am not able to run my application in mobile because it directly goes to access-denied(401) page. I think it is due to a access token issue.

I have mainly 2 interceptors to handle my application. 1.Error interceptor - which handles when there is any route error or unauthorized error. 2. jwt - assign token which internally calls the authentication-service to get token.

Below is my service file where I am trying to get access token in session or local or from the window event by sending a postMessage that i am ready to receive the token.

authentication-service.ts

public getToken() {
    let accessToken = null;
     const auth = JSON.parse(sessionStorage.getItem('auth'));
    if (auth) {
        accessToken = auth.access_token;
    } elseif (accessToken == null || accessToken === undefined) {
        accessToken = localStorage.getItem('access_token');
    }

 window.addEventListener('message', function(event){
      // this event should have all the necessary tokens
 }, false);

 // once my page is loaded to indicate that I am ready to receive the message from server side.
  parent.postMessage({ askForToken:"true"}, "*");

  return accessToken;
}

I am sending a parent.postMessage to make the window.addEventListener to retrive the data but the event is not sending the token as expected.

I am doing all these above code implementation in authentication.service.ts, I am not sure if it is right way to do it.

Can anyone please suggest me the right way to implement this code and receive token suitably?

Please correct me as I am trying deployment with tokens for the first time.

Share Improve this question edited Jun 26, 2019 at 7:37 trungvose 20k9 gold badges50 silver badges84 bronze badges asked Jun 21, 2019 at 7:48 OneraOnera 7174 gold badges16 silver badges36 bronze badges 8
  • Can describe a bit more of your application flow. Basically it will have a login form. If you managed to login to the system, you will have a token and you store it on the localStorage as I suspect. And on every subsequent, you will check for that token and send it together in the header of each http request? Where is the code to redirect user to 401 page, is this inside your guard? – trungvose Commented Jun 24, 2019 at 7:51
  • @trungk18 Yes, the redirect to 401 is in error interceptor guard. And regarding the flow - when i log in, I have a main page and from the main page, I have a link which opens a sub application. When the sub application is about to load I call auth service to post message to parent window (i.e., main page) - from this parent window, I should get a access_token. – Onera Commented Jun 24, 2019 at 10:06
  • So the problem arised when you start to navigate from the main app to the sub app?Did you check the token, is this still a valid one? In my application, I did the same by storing the token inside the localStore and I have an AuthService to manage all of these. But when you got the 401, one thing is for sure that you should redirect user to the login page, not the 401. There are many use cases that we want to force the users to login again and invalidate all the tokens. – trungvose Commented Jun 24, 2019 at 10:33
  • There is only one login entry point for many sub app. The main page team is saying to send a postMessage with the name - 'askForToken' so that they would send me the access_token and I have to do window.addEventListener to get inside event.data.access_token. – Onera Commented Jun 24, 2019 at 11:05
  • Can you please confirm whether all the above code should be written inside the auth service only or it is supposed to be mentioned some other place inside my application? – Onera Commented Jun 24, 2019 at 11:10
 |  Show 3 more ments

1 Answer 1

Reset to default 6 +25

Source code and demo:

https://github./trungk18/angular-authentication-demo-with-lazy-loading

https://stackblitz./edit/angular-authentication-demo-with-lazy-loading

I will add the section to lazy load all the other modules when we first run. Meaning only login page will be loaded first time. After login, the next module will be loaded. It will save us a lot of bandwidth.


There is a User and I will store this whole object into the localStorage once I got it after login successfully.

The flow could be something like.

  1. Open the application

  2. AuthGuard will be triggered. If there is a user object in localStorage with the token, then activate the route. If not then route back to the login page.

  3. Upon the route get activated and started to make API call to the server, the JWTInterceptor will be trigger to send the token on each subsequent request.

  4. ErrorInterceptor check if there is 401, then delete the user from localStorage and reload the page. This handling more on the use case of somebody try to manually update the localStorage with a different token or object. If the token is correct with no modifier from the server, it should not happen.


model

export const ConstValue = { 
    ReturnUrl: "returnUrl",
    CurrentUser: "currentUser",    
}

export const ConstRoutingValue = {
    Login: "login"
}

export interface AICreateUser {
    firstName: string;
    lastName: string;
    email: string;
    password: string;    
    roleIds: string[]
}

export interface PartnerUser extends AICreateUser {
    id: string;    
    createdAt: string;    
    token: string;    
    featureSet: string[]
}

export interface AuthDto {
    email: string;
    password: string;
}

auth.service

export class AuthService {
    private _currentUserSubject: BehaviorSubject<User>;
    public currentUser: Observable<User>;

    public get currentUserVal(): User {
        return this._currentUserSubject.value;
    }

    get currentUserToken() {
        return this.currentUserVal.token;
    }

    constructor(private http: HttpClient) {
        this._currentUserSubject = new BehaviorSubject<User>(this.getUserFromLocalStorage());
        this.currentUser = this._currentUserSubject.asObservable();
    }

    login(username, password) {
        return this.http.post<any>(`/users/authenticate`, { username, password })
            .pipe(map(user => {
                // store user details and jwt token in local storage to keep user logged in between page refreshes
                localStorage.setItem(ConstValue.CurrentUser, JSON.stringify(user));
                this._currentUserSubject.next(user);
                return user;
            }));
    }

    logout() {
        // remove user from local storage and set current user to null
        localStorage.removeItem(ConstValue.CurrentUser);
        this._currentUserSubject.next(null);
    }

    private getUserFromLocalStorage(): User {
        try {
          return JSON.parse(localStorage.getItem(ConstValue.CurrentUser)!);
        } catch (error) {
          return null!;
        }
      }
}

And there is an JwtInterceptor to append the token into header every request

export class JwtInterceptor implements HttpInterceptor {
  constructor(private authenticationService: AuthService) {}

  intercept(
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    let currentUser = this.authenticationService.currentUserVal;
    if (currentUser && currentUser.token) {
      request = request.clone({
        setHeaders: {
          Authorization: `Bearer ${currentUser.token}`
        }
      });
    }

    return next.handle(request);
  }
}

export const JWTInterceptorProvider = {
  provide: HTTP_INTERCEPTORS,
  useClass: JwtInterceptor,
  multi: true
};

ErrorInterceptor to check if there is 401 then delete the user from localStorage reload the page.

export class ErrorInterceptor implements HttpInterceptor {
    constructor(private authenticationService: AuthService) {}

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(request).pipe(catchError(err => {
            if (err.status === 401) {
                // auto logout if 401 response returned from api
                this.authenticationService.logout();
                location.reload(true);
            }
            
            const error = err.error.message || err.statusText;
            return throwError(error);
        }))
    }
}

export const ErrorInterceptorProvider = { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }

Also there is a AuthGuard to make sure you have the token before activate a route. It must be included in all the route when configure the angular router, except the login page.

export class AuthGuard implements CanActivate {
    constructor(
        private router: Router,
        private authenticationService: AuthService
    ) {}

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        const currentUserToken = this.authenticationService.currentUserToken;
        if (currentUserToken) {
            return true;
        }

        // not logged in so redirect to login page with the return url
        this.router.navigate(['/login'], { queryParams: { [ConstValue.ReturnUrl]: state.url }});
        return false;
    }
}

If I want to use the User object, I will take the public observable of currentUser inside the AuthService. For instance I want to show the user user first name on the list

export class AppComponent {
    currentUser: User;

    constructor(
        private router: Router,
        private authenticationService: AuthService
    ) {
        this.authenticationService.currentUser.subscribe(
            x => (this.currentUser = x)
        );
    }

    logout() {
        this.authenticationService.logout();
        this.router.navigate(["/login"]);
    }
}

I hope you get the idea from that.

本文标签: javascriptHow to add a mobile access token in Angular 8 applicationStack Overflow