How to refresh jwt token when it expires flutter [closed] - flutter

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 months ago.
Improve this question
I am trying to send a http request for refresh token automatically when token expires while the user is using app. I want to refresh token in background without disturbing the application.

There are 2 ways to do this.
1.You can store access token expiry in local storage and when the access token expires then you can call an api for new access token.
Make common methods for all the rest api calls and then when token expires there is some specific response you can apply check on that and call again an api for new access token.
Future getCall({required String url}) async {
final Response response = await get(
Uri.parse(url),
headers: {
'token': await LocalStorage.getAccessToken(),
});
if (response.statusCode == 200) {
final data = json.decode(response.body);
return data;
}
else if (response.statusCode == 400 && response.body == "Token Expired")
{
await _getAcessTokenFromRefresh();
await getCall(url: url);
}
}

In addition to #Shubham choudhary answer, you can setup a Request Retrying Logic to all the HTTP requests from the app, so that any failed request gets another chance to be executed again with extra logic around it, this extra logic may be your Refresh Token Logic (The API call to refresh the token and store the new one).
If you are using http package, refer to its add-on package called http_interceptor which provides you with a mechanism for Retrying Requests with an example in the documentation for your case of token expiration:
Sometimes you need to retry a request due to different circumstances, an expired token is a really good example. Here's how you could potentially implement an expired token retry policy with http_interceptor.
class ExpiredTokenRetryPolicy extends RetryPolicy {
#override
Future<bool> shouldAttemptRetryOnResponse(ResponseData response) async {
if (response.statusCode == 401) {
// Perform your token refresh here.
return true;
}
return false;
}
}
If you are using dio then you can do the same logic with also an add-on package called dio_smart_retry, you can refer to its documentation in pub.dev to learn more about it.

Related

How is the Jwt process on the client side using flutter?

I am new using JWT, could someone explain to me how is the process step by step?
I am trying to validate a login and if the password is correct I receive a token with some information.
What do I do with that token in flutter? Do I decode it and use that information or what do I do?
When I make different requests, should I decode the token to do some http and then encode on the client side?
Your help would be greatly appreciated.
here is my code in flutter
// code
Future validateLogin(passwordLogin,emailLogin,BuildContext context) async {
try{
Map<String, String> headers = {
'Content-Type':'application/json;charset=UTF-8',
'Charset':'utf-8'
};
var Url= Uri.parse("http://");
var response = await http.post(Url,
body:{
'password' : passwordLogin,
'email' : emailLogin,
},
).timeout(const Duration(seconds: 90)) ;
var data = json.decode(response.body);
if(data['estado'] == "CORRECT"){
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setBool("CheckEstadoLogin", true);
await prefs.setString("GuardarToken", data['token'].toString());
Navigator.push(context,
new MaterialPageRoute(builder:
(context) => new MenuP())
);
}else {
Fluttertoast.showToast(
msg: "Incorrect",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.green,
textColor: Colors.white,
fontSize: 16.0
);
}
}on TimeoutException catch(e){
print("Tardo mucho la conexion");
}on Error catch(e){
print("Error de Http");
}
}
}
In short, you would find that your backend(if it is indeed providing secure services and not public) will not allow you to consume any webservices unless you have a required access token. So the purpose of jwt in the authorisation flow was to obtain the same that you could have used in the subsequent API calls(mostly the access token is passed as part of the header Authorization). If the APIs you are trying to consume from the backend are public, then there is no need for the access token anyways. So it should be reverse of what you are thinking. The first part is, do you have API calls that needs to be made that will need an access token? If you do, then do the login, get the access token etc, so that it can be used when you make these API calls from your flutter app.
Flutter is the client side. The normal flow when you do a login is, to pass on your user credentials via an interface to a Identity provider in the backend which might use a LDAP or some other data source to validate the user. After which you receive a jwt token back (There can be MFA or other complex secure flows in between(refer oauth2 authorisation code? authorisation code with pkce) but the ultimate objective is to get back a access token.
Now once you have the token, the client is expected to pass this access token in all subsequent Web service calls it makes to the backend. A combination of a gateway + identity provider will ensure to validate that the access token passed for the WS is right before sending the request to the actual server which will service that web service request made by the flutter app.
Decoding jwt at client side, would also give you certain details which the client(flutter app) is already aware of, and may not be of much value.Once again there are best practices in oauth2 on how to refresh this access token on intervals by leveraging a grant etc etc.
This is a good read if you are finding the RFCs a bit complex.
https://auth0.com/docs/get-started/authentication-and-authorization-flow

Authentication flow with Oauth2 in flutter communicating with own api

After some hours of research in vain I stay confused how to do the following:
I have a flutter app which authenticates via OAuth2 to Google (google_sign_in) and Facebook. For Facebook this is the code:
final LoginResult loginResult = await FacebookAuth.instance.login();
final userData = await FacebookAuth.instance.getUserData();
print(userData);
Which prints: {email: john.doe#email.com, id: 123456, name: John Doe}
I already have a webpage with OAuth2 authentication built in Flask/Python. Now I want my users to be able to both use Web and App and share the preferences/data/etc.
How would I achieve that? In my Flask webapp I'm just creating a user in my database if it doesn't exist and then use some authentication headers in subsequent calls. So I thought with the app I could…
send what I got from OAuth to the api and create the user if it does not yet exist
return some sort of token (with a TTL?)
verify the tokens being sent by the app
But this is a lot of custom boilerplate code, I'm sure that this is existing somewhere/somehow. Additionally: How can I be sure someone is not "tampering" my app via decompile, proxying or just plainly calls my api and claiming to be someone else?
My security requirements are medium: The app will eventually have messaging but won't be used for things like money transfer.
I'm considering these options:
PKCE but this looks like the OAuth2 flow would go through my flask api and that sounds too complex (I had a hard time already getting OAuth2 to work in flutter alone)
Resource Owner Password Credentials Grant which sounds like I can somehow pass the results of OAuth2 to my api, get back a token and use this in subsequent requests. However this seems like an outdated protocol (top google results are articles from oracle)
firebase implementation: they use the same flow: first OAuth2 authentication and then passing the credentials into their servers api. On the first time they pass the credentials a user is created and stored in the database, etc. But my reverse engineering skills are not good enough to figure out how it's done.
using a webview and use the oauth2 of my flask website. I'm shying back from this because it would be not a nice mobile experience plus I would not know how to read/store these credentials
After a lot of reading I found a good article on auth0 , in essence there are two options:
Resource Owner Password Flow - use this if you totally trust your app, e.g. when you deploy it to a closed group of users for which you have device management in place. This situation doesn't apply for me and also Auth0 doesn't recommend it. Still, it would have been relatively easy to implement.
PKCE (Proof Key for Code Exchange) - use this when the client cannot be trusted (IMO 99.9% of mobile apps). But this needs some fancy protocol between the mobile app and the server and alone by looking at the flowchart diagram I got headaches
As PKCE looks too complicated to implement myself I decided to go with Firebase, which helps small projects such as mine where you don't want to go through the pain to code the whole PKCE flow yourself.
What I did was:
adding firebase authentication to my flask app, using flask-firebase - this was worth it since it decreased the lines of python code by 40%. Because the module lacks good documentation I wrote this blog post which explains how to use it
adding firebase authentication to flutter. This is very well documented e.g. here
The whole flow then works like this:
flutter triggers the oauth flow for e.g. google
flutter gets back the auth details, including email address, name, etc. (depends on oauth provider)
the auth details are sent to firebase which creates the user if it doesn't exist yet, enriches it with a user id and packs it into an encrypted token
the token is sent to flask, which verifies the token against firebase
flask logs the user in (via flask_login) and returns a session cookie
the session cookie is stored in flutter (using requests) and used for subsequent api calls
to preserve the user logged in even after app close, the session is stored in apps preferences (using shared_preferences)
In essence, this is the code needed (google social login example):
Future<String?> signInWithGoogle() async {
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
final GoogleSignInAuthentication? googleAuth =
await googleUser?.authentication;
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth?.accessToken,
idToken: googleAuth?.idToken,
);
UserCredential userCredentials =
await FirebaseAuth.instance.signInWithCredential(credential);
return userCredentials.user?.getIdToken();
}
…
var cookies = await Requests.getStoredCookies('example.com');
SharedPreferences? prefs;
if (!cookies.keys.contains('session')) {
prefs = await SharedPreferences.getInstance();
if (prefs.containsKey('session')) {
print('cookie not set, load session from prefs');
await Requests.addCookie(
'example.com', 'session', prefs.getString('session')!);
}
}
cookies = await Requests.getStoredCookies('example.com');
if (!cookies.keys.contains('session')) {
print('cookie not set, prefs contain no session, signing in');
String? idToken = await signInWithGoogle();
if (idToken != null) {
await Requests.post('https://example.com/auth/sign-in',
body: idToken,
headers: {'Content-Type': 'application/jwt'},
bodyEncoding: RequestBodyEncoding.PlainText);
var cookies = await Requests.getStoredCookies('example.com');
prefs?.setString('session', cookies['session']!.value);
}
}
var r = await Requests.get('https://example.com/api/something_which_requires_login');
The important part happens with Requests.post: this posts the idToken of firebase to flask, which in turn then verifies the token, calls login_user and returns response with the session cookie header. This cookie is stored by requests and is added to subsequent http requests.
Because this is some mouthful I created this blogpost which explains this in more detail.

How to resend dio request, Cancel Previous request and send again in flutter

final client = Dio();
Future<Voice> getData() async {
final url = 'url';
try {
final response = await client.get(url);
if (response.statusCode == 200) {
return Voice.fromJson(response.data);
} else {
print('${response.statusCode} : ${response.data.toString()}');
throw response.statusCode;
}
} catch (error) {
print(error);
}
}
How can I cancel the previous request and send again, I have a checkbox, Upon checking and unchecking I send requests with different parameters, but the problem is when user check & uncheck 5,10 times quickly application does not work because API request again and again without handling previous one, I want to handle it by cancelling previous request and sending again, no matter how quick he checks & unchecks checkbox I would cancel previous request and send again so in that way application would work fine and would give result according to current state of checkbox, please guide
Your valuable time and guidance will be much appreciated
When you send the request, you can specify the cancellation token (learn more about it here and with this example), but honestly, before you go into cancelling the requests, I'd think about either of the following:
redoing the UI in a way so that the user cannot do check-uncheck the checkbox quickly or
regoing the logic in a way that the request goes out only when the whole form/page is filled/completed or
redoing the logic in a way that the data is available in advance and changing the UI would only cause the local changes.

What does "has been blocked by CORS policy" mean? [duplicate]

This question already has answers here:
No 'Access-Control-Allow-Origin' header is present on the requested resource—when trying to get data from a REST API
(26 answers)
Closed 1 year ago.
This has been marked as a duplicate which is NOT true. This is a mobile app, NOT a web app. There is no originating domain, like the proposed answer has for a web app.
I am trying to use the salesforce api with flutter / dart. I get the following error:
Access to XMLHttpRequest at 'https://instancename.my.salesforce.com/services/data/' from origin 'http://localhost:53765' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
It does not matter if I try to get a session token
void getData() async{
var headers = {
'Authorization': 'Bearer access_token',
};
var res = await http.get('https://instancename.my.salesforce.com/services/data/v20.0/', headers: headers);
if (res.statusCode != 200) throw Exception('http.get error: statusCode= ${res.statusCode}');
print(res.body);
}
or just the version
void getData() async{
Response response=await get("https://instancename.my.salesforce.com/services/data/");
print(response.body);
}
everything fails.
Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any other origins (domain, scheme, or port) than its own from which a browser should permit loading of resources. ... For security reasons, browsers restrict cross-origin HTTP requests initiated from scripts.
you have to set cars policy to your web server not flutter application it happen when you want to connect to you web api on internet if you run your web api on local you'r not get that error

flutter making authenticated api requests

I am trying to do something along these lines to make an authenticated api request:
Future<http.Response> fetchAlbum() {
return http.get(
'https://jsonplaceholder.typicode.com/albums/1',
// Send authorization headers to the backend.
headers: {HttpHeaders.authorizationHeader: "Basic your_api_token_here"},
);
}
I get my api tokens by calling
FirebaseUser user = FirebaseAuth.instance.signInWithEmailAndPassword(email: _email, password: _password).
My questions are:
Don't these tokens expire after a short period of time? How do I make it so my user doesn't have to constantly log in? I don't understand this at all.
Should I save my user variable in a global provider state and access it this way?
I've been watching tons of tutorials on this and I don't get it.