flutter firebase auto refresh sesi pengguna dengan refreshToken

Aug 23 2020

Saya ingin pengguna di aplikasi saya tetap login. Saya menggunakan otentifikasi firebase dengan IDToken yang berlangsung selama 1 jam hingga habis masa berlakunya. Saya ingin menyegarkan sesi setiap kali jika akan kedaluwarsa.

apa yang telah saya baca sejauh ini di sini https://firebase.google.com/docs/reference/rest/auth/#section-refresh-token itu harus mungkin dengan https://securetoken.googleapis.com/v1/token?key=[API_KEY]

Ini adalah kode lengkap saya untuk otentikasi sekarang (flutter)

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import '../provider/http_exception.dart';
import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';

class Auth with ChangeNotifier {
  String _token;
  DateTime _expiryDate;
  String _userId;
  Timer _authTimer;
  bool wasLoggedOut = false;
  bool onBoarding = false;

  Future<void> createUser(String email, String firstName, String lastName) async {
    final url = 'https://test45.firebaseio.com/users/$userId.json?auth=$token';
    final response = await http.put(url, body: json.encode({
      'userEmail': email,
      'userIsArtist': false,
      'userFirstName': firstName,
      'userLastName': lastName,
    }));
    print('post ist done');
    print(json.decode(response.body));
  }

  bool get isAuth {
    return token != null;
  }

  String get userId {
    return _userId;
  }

  String get token {
    if (_expiryDate != null &&
        _expiryDate.isAfter(DateTime.now()) &&
        _token != null) {
      return _token;
    }
    return null;
  }

  Future<void> authenticate(
      String email, String password, String urlSegement) async {
    final url = 'https://identitytoolkit.googleapis.com/v1/accounts:$urlSegement?key=AIzaSyD8pb3M325252dfsDC-4535dfd';

    try {
      final response = await http.post(url,
          body: json.encode({
            'email': email,
            'password': password,
            'returnSecureToken': true,
          }));
      final responseData = json.decode(response.body);
      if (responseData['error'] != null) {
        throw HttpException(responseData['error']['message']);
      }
      _token = responseData['idToken'];
      _userId = responseData['localId'];
      _expiryDate = DateTime.now().add(Duration(seconds: int.parse(responseData['expiresIn'])));
      _autoLogout();
     
      notifyListeners();

      final prefs = await SharedPreferences.getInstance();
      final userData = json.encode({
        'token': _token,
        'userId': _userId,
        'expiryDate': _expiryDate.toIso8601String(),
      });
      prefs.setString('userData', userData);
    } catch (error) {
      throw error;
    }
  }

  Future<void> signup(String email, String password) async {
    return authenticate(email, password, 'signUp');
  }

  Future<void> signin(String email, String password) async {
    return authenticate(email, password, 'signInWithPassword');
  }

  Future<bool> tryAutoLogin() async {
    final prefs = await SharedPreferences.getInstance();
    if(!prefs.containsKey('userData')){
      return false;
    }
    final extractedUserData = json.decode(prefs.getString('userData')) as Map<String, Object>;
    final expiryDate = DateTime.parse(extractedUserData['expiryDate']);

    if(expiryDate.isBefore(DateTime.now())) {
      return false;
    }

    _token = extractedUserData['token'];
    _userId = extractedUserData['userId'];
    _expiryDate = expiryDate;

    notifyListeners();
    _autoLogout();
    return true;
  }


  Future<void> logout() async {
    _token = null;
    _userId = null;
    _expiryDate = null;
    if(_authTimer != null){
      _authTimer.cancel();
      _authTimer = null;
    }
    notifyListeners();
    final prefs = await SharedPreferences.getInstance();
    prefs.remove('userData');
  }

  void _autoLogout() {
    if(_authTimer != null) {
      _authTimer.cancel();
    }
  final timetoExpiry =  _expiryDate.difference(DateTime.now()).inSeconds;
    _authTimer = Timer(Duration(seconds: timetoExpiry), logout);
  }
}

bagaimana cara memodifikasi saya auth.dartuntuk mencapai penyegaran otomatis?

EDIT:

Seperti yang disebutkan dalam komentar, saya bekerja dengan penyedia di mana saya memiliki fungsi berikut untuk mengambil token:

update(String token, id, List<items> itemsList) {
    authToken = token;
    userId = id;
  }

juga di setiap panggilan API saya, saya sudah menggunakan parameter auth:

var url = 'https://test45.firebaseio.com/folder/$inside/$ym.json?auth=$authToken';

Saya hanya perlu seseorang yang dapat menunjukkan cara mengubah kode saya dengan token penyegaran.

Terima kasih sebelumnya!

EDIT:

Saya mencoba menerapkannya, tetapi saya mendapatkan loop tanpa batas, tolong bantu:

String get token {
    if (_expiryDate != null &&
        _expiryDate.isAfter(DateTime.now()) &&
        _token != null) {
      return _token;
    }
    refreshSession();
  }

  Future<void> refreshSession() async {
        final url = 'https://securetoken.googleapis.com/v1/token?key=5437fdjskfsdk38438?grant_type=refresh_token?auth=$token';
      
      try {
      final response = await http.post(url,
          body: json.encode({
  'token_type': 'Bearer',
          }));
      final responseData = json.decode(response.body);
      if (responseData['error'] != null) {
        throw HttpException(responseData['error']['message']);
      }
      _token = responseData['id_token'];
      _userId = responseData['user_id'];
      _expiryDate = DateTime.now().add(Duration(seconds: int.parse(responseData['expires_in'])));
      _autoLogout();
     
      notifyListeners();

      final prefs = await SharedPreferences.getInstance();
      final userData = json.encode({
        'token': _token,
        'userId': _userId,
        'expiryDate': _expiryDate.toIso8601String(),
      });
      prefs.setString('userData', userData);
    } catch (error) {
      throw error;
    }
      }

Jawaban

2 FurkanKURT Sep 19 2020 at 15:58

Saya mengedit refresh_token()fungsi Anda .

Pertama, Anda harus menggunakan kunci api web Anda di proyek firebase dengan tautannya. Anda juga harus menyimpan token penyegaran. Dan jika Anda memposting seperti ini, itu akan berhasil. Jika tidak berhasil, coba tanpa json.encode()fungsi pada tubuh Anda seperti yang saya lakukan.

Future<void> refreshSession() async {
  final url =
      'https://securetoken.googleapis.com/v1/token?key=$WEB_API_KEY'; //$WEB_API_KEY=> You should write your web api key on your firebase project.
  
  try {
    final response = await http.post(
      url,
      headers: {
        "Accept": "application/json",
        "Content-Type": "application/x-www-form-urlencoded"
      },
      body: json.encode({
        'grant_type': 'refresh_token',
        'refresh_token': '[REFRESH_TOKEN]', // Your refresh token.
      }),
      // Or try without json.encode.
      // Like this:
      // body: {
      //   'grant_type': 'refresh_token',
      //   'refresh_token': '[REFRESH_TOKEN]',
      // },
    );
    final responseData = json.decode(response.body);
    if (responseData['error'] != null) {
      throw HttpException(responseData['error']['message']);
    }
    _token = responseData['id_token'];
    _refresh_token = responseData['refresh_token']; // Also save your refresh token
    _userId = responseData['user_id'];
    _expiryDate = DateTime.now()
        .add(Duration(seconds: int.parse(responseData['expires_in'])));
    _autoLogout();

    notifyListeners();

    final prefs = await SharedPreferences.getInstance();
    final userData = json.encode({
      'token': _token,
      'refresh_token': _refresh_token,
      'userId': _userId,
      'expiryDate': _expiryDate.toIso8601String(),
    });
    prefs.setString('userData', userData);
  } catch (error) {
    throw error;
  }
}

Ini adalah file auth.dart lengkap Anda yang telah saya edit.

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import '../provider/http_exception.dart';
import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';

class Auth with ChangeNotifier {
  String _token;
  String _refresh_token;
  DateTime _expiryDate;
  String _userId;
  Timer _authTimer;
  bool wasLoggedOut = false;
  bool onBoarding = false;

  Future<void> createUser(String email, String firstName, String lastName) async {
    final url = 'https://test45.firebaseio.com/users/$userId.json?auth=$token';
    final response = await http.put(url, body: json.encode({
      'userEmail': email,
      'userIsArtist': false,
      'userFirstName': firstName,
      'userLastName': lastName,
    }));
    print('post ist done');
    print(json.decode(response.body));
  }

  bool get isAuth {
    return token != null;
  }

  String get userId {
    return _userId;
  }

  String get token {
    if (_expiryDate != null &&
        _expiryDate.isAfter(DateTime.now()) &&
        _token != null && _refresh_token!=null) {
      return _token;
    }
    refreshSession();
    return null;
  }

  Future<void> authenticate(
      String email, String password, String urlSegement) async {
    final url = 'https://identitytoolkit.googleapis.com/v1/accounts:$urlSegement?key=AIzaSyD8pb3M325252dfsDC-4535dfd'; try { final response = await http.post(url, body: json.encode({ 'email': email, 'password': password, 'returnSecureToken': true, })); final responseData = json.decode(response.body); if (responseData['error'] != null) { throw HttpException(responseData['error']['message']); } _token = responseData['idToken']; _refresh_token = responseData['refreshToken']; _userId = responseData['localId']; _expiryDate = DateTime.now().add(Duration(seconds: int.parse(responseData['expiresIn']))); _autoLogout(); notifyListeners(); final prefs = await SharedPreferences.getInstance(); final userData = json.encode({ 'token': _token, 'refresh_token': _refresh_token, 'userId': _userId, 'expiryDate': _expiryDate.toIso8601String(), }); prefs.setString('userData', userData); } catch (error) { throw error; } } Future<void> signup(String email, String password) async { return authenticate(email, password, 'signUp'); } Future<void> signin(String email, String password) async { return authenticate(email, password, 'signInWithPassword'); } Future<bool> tryAutoLogin() async { final prefs = await SharedPreferences.getInstance(); if(!prefs.containsKey('userData')){ return false; } final extractedUserData = json.decode(prefs.getString('userData')) as Map<String, Object>; final expiryDate = DateTime.parse(extractedUserData['expiryDate']); if(expiryDate.isBefore(DateTime.now())) { return false; } _token = extractedUserData['token']; _refresh_token = extractedUserData['refresh_token']; _userId = extractedUserData['userId']; _expiryDate = expiryDate; notifyListeners(); _autoLogout(); return true; } Future<void> logout() async { _token = null; _refresh_token = null; _userId = null; _expiryDate = null; if(_authTimer != null){ _authTimer.cancel(); _authTimer = null; } notifyListeners(); final prefs = await SharedPreferences.getInstance(); prefs.remove('userData'); } void _autoLogout() { if(_authTimer != null) { _authTimer.cancel(); } final timetoExpiry = _expiryDate.difference(DateTime.now()).inSeconds; _authTimer = Timer(Duration(seconds: timetoExpiry), logout); } Future<void> refreshSession() async { final url = 'https://securetoken.googleapis.com/v1/token?key=$WEB_API_KEY'; 
  //$WEB_API_KEY=> You should write your web api key on your firebase project.
  
  try {
    final response = await http.post(
      url,
      headers: {
        "Accept": "application/json",
        "Content-Type": "application/x-www-form-urlencoded"
      },
      body: json.encode({
        'grant_type': 'refresh_token',
        'refresh_token': '[REFRESH_TOKEN]', // Your refresh token.
      }),
      // Or try without json.encode.
      // Like this:
      // body: {
      //   'grant_type': 'refresh_token',
      //   'refresh_token': '[REFRESH_TOKEN]',
      // },
    );
    final responseData = json.decode(response.body);
    if (responseData['error'] != null) {
      throw HttpException(responseData['error']['message']);
    }
    _token = responseData['id_token'];
    _refresh_token = responseData['refresh_token']; // Also save your refresh token
    _userId = responseData['user_id'];
    _expiryDate = DateTime.now()
        .add(Duration(seconds: int.parse(responseData['expires_in'])));
    _autoLogout();

    notifyListeners();

    final prefs = await SharedPreferences.getInstance();
    final userData = json.encode({
      'token': _token,
      'refresh_token': _refresh_token,
      'userId': _userId,
      'expiryDate': _expiryDate.toIso8601String(),
    });
    prefs.setString('userData', userData);
  } catch (error) {
    throw error;
  }
}
}
2 Rod Aug 25 2020 at 21:35

Anda perlu menyimpan token penyegaran.
Ikuti topik ini untuk menyegarkan IDToken Anda menggunakan token penyegaran:https://firebase.google.com/docs/reference/rest/auth#section-refresh-token

Saat melakukan panggilan apa pun ke API, gunakan fungsi untuk mengambil IDToken. Fungsi ini harus memeriksa apakah IDToken saat ini masih valid dan, jika tidak, minta yang baru (menggunakan tautan yang disediakan).

HưngTrịnh Sep 01 2020 at 11:20

Saya pikir perpustakaan Dio tepat untuk Anda

dio = Dio();
dio.options.baseUrl = URL_API_PROD;
dio.interceptors.add(InterceptorsWrapper(
  onRequest: (Options option) async{
    
    //getToken() : you can check token expires and renew in this function
    await getToken().then((result) {
      token = result;
    });
    option.headers = {
      "Authorization": "Bearer $token"
    };
  }
));

Response response = await dio.get('/api/users');