refresh token circular dependency problem for angular using graphql and apollo client

246 views Asked by At

I'm trying to set up a refresh token strategy to refresh JWT in angular with GraphQL and apollo client when my request returns a 401. Error detection works, but I can't get to the service injection. Angular returns me an error in the console about circular dependency detection

ERROR Error: NG0200: Circular dependency in DI detected for AuthService

and I have no idea how to solve it. There is screen of the error Screen of the error

This is my graphql.module.ts:

import { NgModule } from '@angular/core';
import { APOLLO_OPTIONS } from 'apollo-angular';
import { ApolloLink, InMemoryCache } from '@apollo/client/core';
import { HttpLink } from 'apollo-angular/http';
import { environment } from 'src/environments/environment';
import { setContext } from '@apollo/client/link/context';
import { onError } from "@apollo/client/link/error";
import { AuthService } from './services/auth/auth.service';

const uri = environment.apiUrl + 'graphql'; // <-- add the URL of the GraphQL server here

export function createApollo(httpLink: HttpLink, authService: AuthService) {

  const basic = setContext((operation, context) => ({
    headers: {
      Accept: 'charset=utf-8'
    }
  }));

  const error = onError(({ graphQLErrors, networkError, response, operation }) => {
    if (graphQLErrors[0].extensions.exception.status == 401) {
      authService.refreshToken().subscribe(res => {
        console.log(res)
      })
    }
  })

  const auth = setContext((operation, context) => {
    const token = localStorage.getItem('JWT_TOKEN');

    if (token === null) {
      return {};
    } else {
      return {
        headers: {
          Authorization: `Bearer ${token}`
        }
      };
    }
  });

  const link = ApolloLink.from([basic, error, auth, httpLink.create({ uri })]);
  const cache = new InMemoryCache();

  return {
    link,
    cache
  }
}

@NgModule({
  providers: [
    {
      provide: APOLLO_OPTIONS,
      useFactory: createApollo,
      deps: [HttpLink, AuthService],
    },
  ],
})
export class GraphQLModule { }

And this is my auth.service.ts

import { Injectable } from '@angular/core';
import { Apollo } from 'apollo-angular';
import { LoginResponse, LOGIN_MUTATION, MeQueryResponse, ME_QUERY, RefreshTokenResponse, REFRESH_TOKEN_MUTATION, User } from 'src/app/models/graphql';
import { tap } from 'rxjs/operators';
import { Tokens } from 'src/app/models/tokens';
import { Storage } from '@ionic/storage';
import { Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private readonly JWT_TOKEN = 'JWT_TOKEN';
  private readonly REFRESH_TOKEN = 'REFRESH_TOKEN';
  private loggedUser: string;
  USER = new BehaviorSubject<User>(null);
  isAdmin!: boolean;

  constructor(private apollo: Apollo, private router: Router, private storage: Storage) { }

  login(email: string, password: string) {
    return this.apollo.mutate<LoginResponse>({
      mutation: LOGIN_MUTATION,
      variables: {
        email,
        password
      }
    })
      .pipe(
        tap(tokens => this.doLoginUser(email, tokens.data.login)),
      );
  }


  getMe() {
    return this.apollo.query<MeQueryResponse>({
      query: ME_QUERY
    })
      .pipe(
        tap(user => this.doSaveUser(user.data.me))
      )
  }

  isLoggedIn() {
    return !!this.getJwtToken();
  }

  refreshToken() {
    return this.apollo.mutate<RefreshTokenResponse>({
      mutation: REFRESH_TOKEN_MUTATION
    })
      .pipe
      (
        tap(tokens => this.storeJwtToken(tokens.data.returnToken.token))
      )
  }

  getJwtToken() {
    return localStorage.getItem(this.JWT_TOKEN);
  }

  getRefreshToken() {
    return localStorage.getItem(this.REFRESH_TOKEN);
  }

  logoutUser() {
    this.loggedUser = null;
    this.router.navigate(["/auth/login"])
    this.removeTokens();
  }

  /* doLoadUser() {
    this.storage.get('USER').then(res => {
      if (res) {
        this.USER.next(res);
        this.isAdmin = res.isAdmin;
      }
    })
  } */

  user() {
    return this.USER;
  }

  private doSaveUser(user: User) {
    this.USER.next(user);
    this.isAdmin = user.isAdmin;
    this.storage.set('USER', user);
  }

  private doLoginUser(email: string, tokens: Tokens) {
    this.loggedUser = email;
    this.storeTokens(tokens);
  }

  private storeJwtToken(jwt: string) {
    localStorage.setItem(this.JWT_TOKEN, jwt);
  }

  private storeTokens(tokens: Tokens) {
    localStorage.setItem(this.JWT_TOKEN, tokens.token);
    localStorage.setItem(this.REFRESH_TOKEN, tokens.refreshToken);
  }

  private removeTokens() {
    localStorage.removeItem(this.JWT_TOKEN);
    localStorage.removeItem(this.REFRESH_TOKEN);
  }
}

Any ideas? Any help would be greatly appreciated

0

There are 0 answers