Obtain a new token by refresh token with google_sign_in Flutter - flutter

I'm writing an application that call google fit rest api from Flutter.
I need to sign with google using (https://pub.dev/packages/google_sign_in).
I can obtain a token without problem (see Did anyone manage to get the id token from google sign in (Flutter)) but how to obtain a new token when it is expired?
I dont' want to ask to the user to login and obtain a new token every hour

You can do this in 2 ways.
You can use the API.
I don't know if this is standard but you can do a silent login every time the user opens the app, the silent log logs into the user account without user interaction and this way you have a new token. Like this:
Future<String> refreshToken() async {
print("Token Refresh");
final GoogleSignInAccount googleSignInAccount =
await googleSignIn.signInSilently();
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final AuthResult authResult = await auth.signInWithCredential(credential);
return googleSignInAuthentication.accessToken; // New refreshed token
}

Get refresh token manually
apiKey - your api key from firebase,
idToken - unexpired id token,
provider - 'google.com', 'apple.com' etc
final url = Uri.parse('https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp?key=$apiKey');
final response = await http.post(
url,
headers: {'Content-type': 'application/json'},
body: jsonEncode({
'postBody': 'id_token=$token&providerId=$provider',
'requestUri': 'http://localhost',
'returnIdpCredential': true,
'returnSecureToken': true
})
);
if (response.statusCode != 200) {
throw 'Refresh token request failed: ${response.statusCode}';
}
final data = Map<String, dynamic>.of(jsonDecode(response.body));
if (data.containsKey('refreshToken')) {
// here is your refresh token, store it in a secure way
} else {
throw 'No refresh token in response';
}

Before calling '.signIn()' method make sure that GoogleSignInAccount variable type has 'forceCodeForRefreshToken' set up with true.
After that take 'serverAuthCode' and make a request to https://accounts.google.com/o/oauth2/token with the next body
'access_type': 'offline', tokenType: <serverAuthCode>, 'grant_type': 'code', 'client_secret': <yourClientSecret>, 'client_id': <yourClientId>, 'redirect_uri': <yourMiddleWare>,.
In response you will get that refresh_token.

Related

Flutter Google sign-in error on first time login

I am using google_sign_in plugin. It's running fine if the user has already account logged in and user just needs to select his account.
But when a new google account is added just before the login, then googleUser remains null and hence it throws exception.
Here's the code.
Future<UserCredential> signInWithGoogle() async {
//here googleUser remains null on first time login.....
GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
// Obtain the auth details from the request
final GoogleSignInAuthentication? googleAuth =
await googleUser?.authentication;
final OAuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth?.accessToken,
idToken: googleAuth?.idToken,
);
// Once signed in, return the UserCredential
return await FirebaseAuth.instance.signInWithCredential(credential);
}

After login with twitter authorize api, returning is black screen

I am going to develop a flutter app, it login/ signup with twitter api. I code it prefectly but when it move to authorize window to back then it show black screen not returning to my app, whats happing?? Thank you My code is
Future<UserCredential> signInWithTwitter() async {
// Create a TwitterLogin instance
final twitterLogin = new TwitterLogin(
apiKey: 'wkeO1wbI1dHfEjK9TMXPdbD4g',
apiSecretKey: 'b60VwlAerBi39M2TBKz1V6bDAbzu2J2vWFupWs2ZSl6Quz6uVf',
redirectURI: 'school-management-system://',
);
// Trigger the sign-in flow
final authResult = await twitterLogin.login();
// Create a credential from the access token
final twitterAuthCredential = TwitterAuthProvider.credential(
accessToken: authResult.authToken!,
secret: authResult.authTokenSecret!,
);
// Once signed in, return the UserCredential
return await FirebaseAuth.instance
.signInWithCredential(twitterAuthCredential);
}
I did this code and its output in video[output here]

Is it possible to fetch emails from pop3 using sign in with google rather than app password?

I am fetching emails from pop3 with app password which is working perfectly fine. Can I replace app password with sign-in with google? I am using enough_email package.
I am done like this:
await popClient.connectToServer(host, port, isSecure: true);
await popClient.login(email!, token!);
and signing with google like this:
FirebaseAuth auth = FirebaseAuth.instance;
User? user;
final GoogleSignIn googleSignIn = GoogleSignIn();
final GoogleSignInAccount? account = await googleSignIn.signIn();
final GoogleSignInAuthentication authentication = await account.authentication;
final AuthCredential authCredential = GoogleAuthProvider.credential(
accessToken: authentication.accessToken,
idToken: authentication.idToken,
);
final UserCredential userCredential = await auth.signInWithCredential(authCredential);
user = userCredential.user;
I already tried to login with accessToken and idToken but getting PopException -ERR [AUTH] Username and password not accepted
After some research I found the following documents
https://developers.google.com/gmail/imap/xoauth2-protocol
https://developers.google.com/identity/protocols/oauth2
that leads me to the right direction. To authenticate client using sign-in with google. Here is my code for reference:
ImapClient imapClient = ImapClient();
await imapClient.connectToServer("imap.google.com", 993, isSecure: true);
await imapClient.authenticateWithOAuth2(user, accessToken);
await imapClient.selectInbox();
final FetchImapResult result = await imapClient.fetchRecentMessages();
List<MimeMessage> mimeMessages = result.messages;
for (MimeMessage mimeMessage in mimeMessages) {
print(mimeMessage.decodeTextPlainPart());
}
await imapClient.disconnect();
user: email address
accessToken: google sign-in authentication token

Retrieving the Authorization Code from Fitbit API with Flutter

I am trying to build an app with flutter that uses Fitbit API, I tried different packages to do Web Authentication like Fitbitter that uses flutter-web-auth for authentication. Also tried web-view Widget.
in case of Fitbitter :
the issue is when I logged in the fitbit account and get the response that content authorization code https://example.com/callback?code=<authorization_code>#_=_0.
authorize method in FitbitConnector class doesn't redirect me back to the app with authorization code instead stays in the chrome custom tab.
authorize method
static Future<String?> authorize(
{BuildContext? context,
String? clientID,
String? clientSecret,
required String redirectUri,
required String callbackUrlScheme}) async {
// Instantiate Dio and its Response
Dio dio = Dio();
Response response;
String? userID;
// Generate the fitbit url
final fitbitAuthorizeFormUrl = FitbitAuthAPIURL.authorizeForm(
userID: userID, redirectUri: redirectUri, clientID: clientID);
// Perform authentication
try {
final result = await FlutterWebAuth.authenticate(
url: fitbitAuthorizeFormUrl.url!,
callbackUrlScheme: callbackUrlScheme);
//Get the auth code
final code = Uri.parse(result).queryParameters['code'];
// Generate the fitbit url
final fitbitAuthorizeUrl = FitbitAuthAPIURL.authorize(
userID: userID,
redirectUri: redirectUri,
code: code,
clientID: clientID,
clientSecret: clientSecret);
response = await dio.post(
fitbitAuthorizeUrl.url!,
data: fitbitAuthorizeUrl.data,
options: Options(
contentType: Headers.formUrlEncodedContentType,
headers: {
'Authorization': fitbitAuthorizeUrl.authorizationHeader,
},
),
);
// Debugging
final logger = Logger();
logger.i('$response');
// Save authorization tokens
final accessToken = response.data['access_token'] as String;
final refreshToken = response.data['refresh_token'] as String;
userID = response.data['user_id'] as String?;
GetIt.instance<SharedPreferences>()
.setString('fitbitAccessToken', accessToken);
GetIt.instance<SharedPreferences>()
.setString('fitbitRefreshToken', refreshToken);
} catch (e) {
print(e);
} // catch
return userID;
}
Do you know a way to do web authentication and get redirected to the app with user Token and ID?

How to obtain the refreshToken when google sign in with Flutter / Firebase

I've already did the google sign with the firebase flutter toolkit. When sign in is done, I receive the idToken to read/write on the database.
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
Future<String> signInWithGoogle() async {
await Firebase.initializeApp();
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final UserCredential authResult = await _auth.signInWithCredential(credential);
final User user = authResult.user;
final idToken = await user.getIdToken();
}
The problem is, I need to auto login user when he opens the app again. So, when I sign in with google, I do not receive the refresh token, needed to get the new valid idToken, as the Doc.
How can I get the refreshToken ?
You can use SharedPreferences to set a bool value when a user is logged in from google and check the value when the app restarts. Don't forget to remove this value from shared preferences when user logged out.
I'm pretty sure googl_sign_in keeps the refresh token internally and you always receive a valid idToken. If it's about to expire, you can force renewal.
Answering your direct question about refreshToken:
You can create your own implementation of the google_sign_in class. In the end it's just an OAuth token. Here's a web implementation of a custom google_sign_in service: https://stackoverflow.com/a/69746036/5492496
I'm pretty sure you can do the same for Android & iOS using web view, you will loose the single click functionality on Android though.