Flutter how can i set Auth token from flutter secure storage to dio header?

3k Views Asked by At

After login i setting user token to my user Secure storage. Like :

Future<AuthResponseModel?> login(AuthRequstModel model) async {
    try {
      Response response = await _dio.post(loginPath, data: model);
      if (response.statusCode == 200) {
        final AuthResponseModel authResponseModel = AuthResponseModel.fromJson(response.data);
        if (authResponseModel.success!) {
          await UserSecureStorage.setField("token", authResponseModel.token);
          
        }
        return AuthResponseModel.fromJson(response.data);
      }

      return null;
    } catch (e) {
      return null;
    }
  }

User Secure Storage =>

class UserSecureStorage {
  static const _storage = FlutterSecureStorage();

  static Future setField(String key, value) async {
    await _storage.write(key: key, value: value);
  }
 static Future<String?> getField(key) async {
    return await _storage.read(key: key);
  }

But problem is when i want to make apiservice and when i want to auth token inside header of dio, I cant access it becouse its a future<String?> function. But i cant use await coz its inside of baseoption. Like :

class ApiService {
  final _dio = Dio(BaseOptions(headers: {
    'authorization': 'Bearer ${UserSecureStorage.getField("token")}', //I cant access here its only giving instance.
  }));
  Future<Response?> get(String path) async {
    try {
      Response response = await _dio.get('${ApiConstants.BASE_URL}$path');
      if (response.statusCode == 200) {
        return response;
      }
      return null;
    } on DioError catch (e) {
      return null;
    }
  }

What can i do for solve that problem ? I tried use .then(value=>value) after tried get token but didnt work too. Thanks for responses!

4

There are 4 best solutions below

1
On BEST ANSWER

I think token is not getting updated because _dio is already intitalized.

Try to request for token when dio request is made like :

class ApiService {
  final _dio = Dio();
  Future<Response?> get(String path) async {
    try {
      Response response = await _dio.get('${ApiConstants.BASE_URL}$path', options: Options(headers: {"authorization": "Bearer ${UserSecureStorage.getField("token")}"}));
      if (response.statusCode == 200) {
        return response;
      }
      return null;
    } on DioError catch (e) {
      return null;
    }
  }
1
On

your are getting instance because UserSecureStorage.getField("token") is future so you can get token when you put await keyword

so try like this

await UserSecureStorage.getField("token")

0
On

Use options in get method to add headers for a single request or interceptors for all requests.

0
On

I think that it is not an issue easily solvable, I would try with two different methods, you can maintain the token in a state manager such as Provider so you don't have to rely on an async function to retrive it, but this of course add in the code the state manager structure that complicates thing a little.

A bit more naive way to solve this could be to include a async initializator in the ApiService class such this

class ApiService {

  late final _dio;

  Future<void> init() async {
  _dio = Dio(BaseOptions(headers: {
    'authorization': 'Bearer ${UserSecureStorage.getField("token")}', //I cant access here its only giving instance.
  }));}

  Future<Response?> get(String path) async {
    try {
      Response response = await _dio.get('${ApiConstants.BASE_URL}$path');
      if (response.statusCode == 200) {
        return response;
      }
      return null;
    } on DioError catch (e) {
      return null;
    }
  }

And this introduce us a new issue, we have to call init everytime the class ApiService is instantiated, to solve this you could use the package get_it which grants you the possibility to instatiate only once the class and access it from everywhere in your project.

I hope this will help you solve your problem