Как отправить firebase jwt getIdToken на http получить запрос в angular 8? - PullRequest
0 голосов
/ 25 октября 2019

Я использую Angular 8 для создания SPA.

Firebase используется для аутентификации пользователя как в клиенте, так и в бэкэнде, поэтому мне нужно отправить токен jwt в запросе http.getк бэкэнду для аутентификации пользователя.

Бэкэнд - это API, созданный с использованием django 2.2 и django rest framework, который отправляет API для использования в клиентском приложении.

auth.service.ts

@Injectable({
  providedIn: 'root'
})

export class AuthService {
  userData: any; // Save logged in user data
  public userToken: string;

  constructor(
    public afs: AngularFirestore,   // Inject Firestore service
    public afAuth: AngularFireAuth, // Inject Firebase auth service
    public router: Router,
    public ngZone: NgZone // NgZone service to remove outside scope warning
  ) {
    /* Saving user data in localstorage when 
    logged in and setting up null when logged out */
    this.afAuth.authState.subscribe(user => {
      if (user) {
        this.userData = user;
        localStorage.setItem('user', JSON.stringify(this.userData));
        JSON.parse(localStorage.getItem('user'));
      } else {
        localStorage.setItem('user', null);
        JSON.parse(localStorage.getItem('user'));
      }
    });

  }


  GetToken(): string {
    this.afAuth.auth.onAuthStateChanged( user => {
      if (user) {
        user.getIdToken().then(idToken => {
          this.userToken = idToken;

            // this shows the userToken
          console.log('token inside getToken method ' + this.userToken);
        });
      }
    });

    // this shows userToken as undefined
    console.log('before return ' + this.userToken);
    return this.userToken;
  }

}

api.service.ts

@Injectable({
  providedIn: 'root'
})
export class ApiService {

  private url = environment.baseUrl;
  token: any;
  data: any;

  constructor(
    private http: HttpClient,
    private authService: AuthService,
    ) {}



    // old method to get emloyees data
  // public getEmployees(): Observable<Employee[]> {
  //   return this.http.get<Employee[]>(`${this.url}/employee/`);
  // }


  httpOptions = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json',
      'Authorization': 'JWT ' + this.authService.GetToken()
    }),
  };


  public getEmployees(): Observable<Employee[]> {

      // token is undefined here
    console.log('token inside getEmployees method ' + this.token);

    return this.http.get<Employee[]>(`${this.url}/employee/`, this.httpOptions);
  }

}

Бэкэнд работает отлично, что я проверил, добавив токен в httpOptions, например:

httpOptions = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json',
      'Authorization': 'JWT ' + 'ey.....'
    }),
  };

Нокогда я пытаюсь сделать то же самое, что указано в коде, это не работает. Токен пользователя остается неопределенным.

Ответы [ 2 ]

2 голосов
/ 25 октября 2019

Здесь не определено console.log('before return ' + this.userToken);, потому что getIdToken() возвращает Promise, что означает, что он асинхронный, поэтому единственный способ получить доступ к userToken - это метод then().

1 голос
/ 25 октября 2019

Ответ Питера имеет суть: getIdToken() является асинхронным, поэтому к тому времени, как ваш return this.userToken; запускается, this.userToken = idToken; еще не запускается. Это должно быть видно из результатов ваших console.log операторов.

Подробнее об этом см. Как вернуть значение из функции асинхронного обратного вызова? Я настоятельно рекомендую изучить этот ответ. на некоторое время, поскольку это асинхронное поведение невероятно распространено при работе с веб-API.

Исправление для вашего кода - возвращать Promise вместо попытки вернуть значение:

GetToken(): string {
  return new Promise((resolve, reject) => {
    this.afAuth.auth.onAuthStateChanged( user => {
      if (user) {
        user.getIdToken().then(idToken => {
          this.userToken = idToken;
          resolve(idToken);    
        });
      }
    });
  })
}

Другими словами: GetToken возвращает обещание, которое разрешается, как только токен ID становится доступным. Если вы знаете , что пользователь уже вошел в систему при вызове этой функции, вы можете упростить ее до:

GetToken(): string {
    const user = firebase.authentication().currentUser;
    return user.getIdToken()
}

Разница в том, что вторая функция не ожидает, пока пользовательбудет выполнен вход, поэтому произойдет сбой, если пользователь не вошел в систему.

Затем вы будете использовать любую из перечисленных выше функций, например, в getEmployees:

public getEmployees(): Observable<Employee[]> {
  return new Promise((resolve, reject) => 
    this.authService.GetToken().then((idToken) => {
      httpOptions = {
        headers: new HttpHeaders({
          'Content-Type': 'application/json',
          'Authorization': 'JWT ' + idToken
        }),
      };

      this.http.get<Employee[]>(`${this.url}/employee/`, this.httpOptions)
          .then(resolve).catch(reject);

    })
  })
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...