I'm trying to add facebook login to the flutter firebase_auth plugin.
Here is an existing Google sign in method which runs fine:
private void handleSignInWithGoogle(MethodCall call, final Result result) {
#SuppressWarnings("unchecked")
Map<String, String> arguments = (Map<String, String>) call.arguments;
String idToken = arguments.get("idToken");
String accessToken = arguments.get("accessToken");
AuthCredential credential = GoogleAuthProvider.getCredential(idToken, accessToken);
firebaseAuth
.signInWithCredential(credential)
.addOnCompleteListener(activity, new SignInCompleteListener(result));
}
Here I added a Facebook sign in method which generates the error below:
private void handleSignInWithFacebook(MethodCall call, final Result result) {
#SuppressWarnings("unchecked")
Map<String, String> arguments = (Map<String, String>) call.arguments;
String accessToken = arguments.get("accessToken");
AuthCredential credential = FacebookAuthProvider.getCredential(accessToken);
firebaseAuth
.signInWithCredential(credential)
.addOnCompleteListener(activity, new SignInCompleteListener(result));
}
Here is the full PR and a link to the build error on travis-ci: https://github.com/flutter/plugins/pull/184
Here is the error generated locally:
/Users/frederickcook/GondolaProjects/Eng/plugins/packages/firebase_auth/android/src/main/java/io/flutter/firebaseauth/FirebaseAuthPlugin.java:120: error: cannot find symbol
AuthCredential credential = FacebookAuthProvider.getCredential(accessToken);
^
symbol: variable FacebookAuthProvider
location: class FirebaseAuthPlugin
Here is the error on travis-ci: https://travis-ci.org/flutter/plugins/builds/257319164
Note that both GoogleAuthProvider and FacebookAuthProvider are classes in the firebase auth package: https://firebase.google.com/docs/reference/android/com/google/firebase/auth/package-summary
import com.google.firebase.auth.FacebookAuthProvider; must additionally be added to file header to access the FacebookAuthProvider class.
Related
I am using this code to write the login info into flutter secure storage when login success in macos desktop app:
static void saveAuthInfo(Response response, String username, String password) {
Map result = response.data["result"];
String accessToken = result["accessToken"];
String refreshToken = result["refreshToken"];
String registerTime = result["registerTime"];
SecureStorageUtil.putString("username", username);
SecureStorageUtil.putString("password", password);
SecureStorageUtil.putString("accessToken", accessToken);
SecureStorageUtil.putString("refreshToken", refreshToken);
SecureStorageUtil.putString("registerTime", registerTime);
}
the flutter secure storage version was 5.0.1 right now which I am using current. But when I tried to read the value, the returned result value was null.
static Future<AppUser> currentUser() async {
String? userName = await SecureStorageUtil.getString("username");
String? registerTime = await SecureStorageUtil.getString("registerTime");
AppUser user = new AppUser(phone: userName, registerTime: registerTime);
return user;
}
the SecureStorageUtil class look like this:
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class SecureStorageUtil{
static FlutterSecureStorage _preferences = FlutterSecureStorage();
static Future<String?> getString (String key, {String defValue = ''}) {
return _preferences.read(key:key) ;
}
static Future<void> putString(String key, String value) {
return _preferences.write(key:key, value:value);
}
static Future<void> delString(String key) {
return _preferences.delete(key:key);
}
}
Am I missing something? what should I do to make the flutter secure storage put and fetch value? I could not use the 5.0.2 version because this version of flutter sececure storage could not compile in android(I tried the version 5.0.2 still did not work).
I have a cross platform application (mobile, desktop and web) created in Flutter that I would like to set up to be authenticated with Azure AD.
I know that there are some packages that you can add for mobile and maybe even for web but I am unable to find a working solution for desktop.
I thought that I could open the browser on the device and use that to sign the user in, but it would need a URI to redirect to when the user is authenticated and for the application to be able to get the token that I can then use to make calls to my API. I can't see how that would work though, due to the application being hosted on the users device and not on a server with a set IP like with websites.
Any possible solutions or guidance would be greatly appreciated.
I ended up using a combination of this older tutorial for Facebook authentication along with Microsoft documentation on how to get a token for native apps to create a small authenticating service seen below.
I used the following pub packages:
url_launcher
flutter_dotenv
http
Auth Service:
import 'dart:async';
import 'dart:io';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:research_library_viewer/Models/Token.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:http/http.dart' as http;
class AuthenticationService {
String tenant = dotenv.env['MSAL_TENANT']!;
String clientId = dotenv.env['MSAL_CLIENT_ID']!;
String clientSecret = dotenv.env['MSAL_CLIENT_SECRET']!;
String redirectURI = dotenv.env['MSAL_LOGIN_REDIRECT_URI']!;
String scope = dotenv.env['MSAL_CLIENT_SCOPE']!;
String authority = dotenv.env['MSAL_AUTHORITY_URI']!;
Future<Stream<String>> _server() async {
final StreamController<String> onCode = StreamController();
HttpServer server =
await HttpServer.bind(InternetAddress.loopbackIPv4, 8080);
server.listen((HttpRequest request) async {
final String? code = request.uri.queryParameters["code"];
request.response
..statusCode = 200
..headers.set("Content-Type", ContentType.html.mimeType)
..write("<html><h1>You can now close this window</h1></html>");
await request.response.close();
await server.close(force: true);
if (code != null) {
onCode.add(code);
await onCode.close();
}
});
return onCode.stream;
}
String getAuthUrl() {
String authUrl =
"http://$authority/$tenant/oauth2/v2.0/authorize?client_id=$clientId&response_type=code&redirect_uri=$redirectURI&response_mode=query&scope=$scope";
return authUrl;
}
Map<String, dynamic> getTokenParameters(String token, bool refresh) {
Map<String, dynamic> tokenParameters = <String, dynamic>{};
tokenParameters["client_id"] = clientId;
tokenParameters["scope"] = scope;
tokenParameters["client_secret"] = clientSecret;
if (refresh) {
tokenParameters["refresh_token"] = token;
tokenParameters["grant_type"] = "refresh_token";
} else {
tokenParameters["code"] = token;
tokenParameters["redirect_uri"] = redirectURI;
tokenParameters["grant_type"] = "authorization_code";
}
return tokenParameters;
}
Future<Token> getToken() async {
String url = getAuthUrl();
Stream<String> onCode = await _server();
if (await canLaunch(url)) {
await launch(url);
} else {
throw "Could not launch $url";
}
final String code = await onCode.first;
final Map<String, dynamic> tokenParameters =
getTokenParameters(code, false);
final response = await http.post(
Uri.https(
'login.microsoftonline.com',
'$tenant/oauth2/v2.0/token',
),
headers: <String, String>{
'Content-Type': 'application/x-www-form-urlencoded'
},
body: tokenParameters);
if (response.statusCode == 200) {
return tokenFromJson(response.body);
} else {
throw Exception('Failed to acquire token');
}
}
Future<Token> refreshToken(String? refreshToken) async {
if (refreshToken == null) {
return getToken();
} else {
final Map<String, dynamic> tokenParameters = getTokenParameters(refreshToken, true);
final response = await http.post(
Uri.https(
'login.microsoftonline.com',
'$tenant/oauth2/v2.0/token',
),
headers: <String, String>{
'Content-Type': 'application/x-www-form-urlencoded'
},
body: tokenParameters);
if (response.statusCode == 200) {
return tokenFromJson(response.body);
} else {
throw Exception('Failed to acquire token');
}
}
}
}
Token:
import 'dart:convert';
Token tokenFromJson(String str) {
final jsonData = json.decode(str);
return Token.fromJson(jsonData);
}
class Token {
String accessToken;
String tokenType;
num? expiresIn;
String? refreshToken;
String? idToken;
String? scope;
Token({
required this.accessToken,
required this.tokenType,
this.expiresIn,
this.refreshToken,
this.idToken,
this.scope,
});
factory Token.fromJson(Map<String, dynamic> json) => Token(
accessToken: json["access_token"],
tokenType: json["token_type"],
expiresIn: json["expires_in"],
refreshToken: json["refresh_token"],
idToken: json["id_token"],
scope: json["scope"],
);
Map<String, dynamic> toJson() => {
"access_token": accessToken,
"token_type": tokenType,
"expires_in": expiresIn,
"refresh_token": refreshToken,
"id_token": idToken,
"scope": scope,
};
}
I think that this could still be improved a lot, but it is definitely something to start with if you are sitting with a similar challenge.
Found an MS document you can follow to add Azure Authentication in your Desktop application.
Refer this : Sign-in a user with the Microsoft Identity Platform in a WPF Desktop application and call an ASP.NET Core Web API
There is also another way for the same but with Azure AD B2C : Configure authentication in a sample WPF desktop app by using Azure AD B2C
The application registration and architecture are illustrated in the following diagrams:
I create Auth Class for firebase Auth version 0.11.1+10, now after update the packege firebase auth to last version some code is error.
Future<String> createUserWithEmailAndPassword(
String email, String password, String name) async {
final currentUser = await _firebaseAuth.createUserWithEmailAndPassword(
email: email,
password: password,
);
//updat username is error code ***************************
var userUpdateInfo = UserUpdateInfo();
userUpdateInfo.displayname = name;
await currentUser.updateProfile(userUpdateInfo);
await currentUser.reload();
return currentUser.uid;
}
See Error
UserUpdateInfo is now deprecated but you can use this instead
await FirebaseAuth.instance.currentUser.updateProfile();
And you pass the info you want to update to updateProfile()
I'm trying to integrate Keycloak authentication in my Flutter app using simple_auth_flutter, my problem is that I don't know how not to declare the clietSecret to be passed to simpleAuth.
Here's my example code:
import 'package:simple_auth/simple_auth.dart' as simpleAuth;
Future<void> _logIn() async {
final String identifier = 'keycloak';
final String clientId = 'flutter-client';
final String clientSecret = 'my-long-key-secret-krom-keycloak-admin-panel';
final String redirectUrl = 'http://10.0.2.2:4180/oauth2/callback';
final String baseUrl = 'http://10.0.2.2:8090';
final String realm = 'master';
simpleAuth.KeycloakApi api = simpleAuth.KeycloakApi(identifier, clientId,
clientSecret, redirectUrl, baseUrl, realm);
try {
var success = await api.authenticate();
print('Result: ' + success.toString());
} catch (e) {
print('Exception: ' + e.toString());
}
}
It shows the login page correctly and logs in, but it isn't a good idea to write the clientSecret in cleartext.
Any suggestion of how I should rewrite the code?
I need use Auth0 with Flutter but there is no such SDK in Auth0 site.
Auth0 works to create such SDK for Flutter.
Did anyone use Auth0 with Flutter or what can you advise?
Its very simple to get started with flutter auth0
Have a class for auth0 and call this at the places you need them. But also be sure to set the constants AUTH0_DOMAIN, AUTH0_CLIENT_ID, AUTH0_REDIRECT_URI, AUTH0_ISSUER
class Auth0 {
final FlutterAppAuth appAuth = FlutterAppAuth();
Map<String, Object> parseIdToken(String idToken) {
final List<String> parts = idToken.split('.');
assert(parts.length == 3);
return jsonDecode(
utf8.decode(base64Url.decode(base64Url.normalize(parts[1]))));
}
Future<Map<String, Object>> getUserDetails(String accessToken) async {
const String url = 'https://$AUTH0_DOMAIN/userinfo';
final http.Response response = await http.get(
url,
headers: <String, String>{'Authorization': 'Bearer $accessToken'},
);
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Failed to get user details');
}
}
Future<void> loginAction() async {
isBusy = true;
errorMessage = 'Error! - ';
try {
final AuthorizationTokenResponse result =
await appAuth.authorizeAndExchangeCode(
AuthorizationTokenRequest(
AUTH0_CLIENT_ID,
AUTH0_REDIRECT_URI,
issuer: 'https://$AUTH0_DOMAIN',
scopes: <String>['openid', 'email', 'profile', 'offline_access'],
promptValues: ['login']
),
);
final Map<String, Object> idToken = parseIdToken(result.idToken);
final Map<String, Object> profile =
await getUserDetails(result.accessToken);
isBusy = false;
name = idToken['name'];
email = profile['email'];
picture = profile['picture'];
} on Exception catch (e, s) {
print('login error: $e - stack: $s');
isBusy = false;
errorMessage = e.toString();
}
}
Instead of using a boolean for checking isLoggedIn try saving the token in the localstorage and that will set the state as is.
There's an auth0 package for flutter to use Auth0 API provides login, logout and access APIs for authentication in your App. However, you need to make changes inside android and ios files in your flutter project. You need to configure your callbacks and application settings for that, The author has their example on github that you should check out.
I would advise you to follow the blog post provided by the Auth0 team -
Get Started with Flutter Authentication
For Flutter Web App, I am making a wrapper around Auth0 JS SPA SDK.
GitHub: https://github.com/anthonychwong/auth0-flutter-web
Pub.dev: https://pub.dev/packages/auth0_flutter_web
import 'package:auth0_flutter_web/auth0_flutter_web.dart';
Auth0 auth0 = await createAuth0Client(
Auth0CreateOptions(
domain: '-- domain of the universal login page --',
client_id: '-- id of your app --',
)
);
String token = await auth0.getTokenWithPopup();
It is in very early stage and PRs are welcome.