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?
Related
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?
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 can't get user's location using facebook sign in. I used 'user_location' and graph API for getting the location but I can't seem to get the output. It only returns null. It would be great if you would help me..
This is my code:
void _signInFacebook() async {
FacebookLogin facebookLogin = FacebookLogin();
final result = await facebookLogin.logIn(['email', 'public_profile', 'user_birthday', 'user_location']);
final token = result.accessToken.token;
final graphResponse = await http.get('https://graph.facebook.com/v2.12/me?fields=name,first_name,last_name,birthday,location,email&access_token=${token}');
final profile = json.decode(graphResponse.body);
var fb_ID = profile["id"];
var accessToken = token;
if (result.status == FacebookLoginStatus.loggedIn) {
final credential = FacebookAuthProvider.getCredential(accessToken: token);
_auth.signInWithCredential(credential);
// connecting to mongo database
AuthService().facebookLogin(fb_ID, accessToken).then((val) {
if (val.data['success']) {
var token2 = val.data['token'];
AuthService().getInfo(token2).then((val) async {
var store = val.data['user'];
},
);
} else {
print('WRONG EMAIL/ PASS');
return;
}
});
}
}
I am using mailer package from dart pub. I created a service account and copied the content of json file into class variable CLIENT_JSON. You can see some fields of CLIENT_JSON in getAccessToken() function.
Whenever I try to email someone, an exception is thrown
SmtpClientAuthenticationException (Incorrect username / password / credentials)
Here are two functions I am calling from UI.
Future<AccessCredentials> getAccessToken() async {
var accountCredentials = ServiceAccountCredentials.fromJson({
"private_key_id": CLIENT_JSON["private_key_id"],
"private_key": CLIENT_JSON["private_key"],
"client_email": CLIENT_JSON["client_email"],
"client_id": CLIENT_JSON['client_id'],
"type": "service_account"
});
AccessCredentials accessCredentials;
final client = http.Client();
try {
accessCredentials = await obtainAccessCredentialsViaServiceAccount(
accountCredentials, ["https://mail.google.com/"], client);
print("[EMAIL_SERVICE] Access Token Fetched");
} on Exception catch (err) {
print("[EMAIL_SERVICE] Error in fetching access token. Error: $err");
}
client.close();
return accessCredentials;
}
Future<void> sendEmailFromConfibuddy({
#required String receiverEmail,
#required String subject,
#required String body,
}) async {
final credentials = await getAccessToken();
if (credentials == null) {
print("[EMAIL_SERVICE] Credentials are null.");
return;
}
final smtpServer = gmailSaslXoauth2(
CLIENT_JSON["client_email"], credentials.accessToken.data);
final message = Message()
..from = Address(CLIENT_JSON["client_email"], 'Confibuddy')
..recipients.add("example#gmail.com")
..subject = subject
..html = body;
try {
final sendReport = await send(message, smtpServer);
print('Message sent: ' + sendReport.toString());
} on MailerException catch (e) {
print('Message not sent.');
for (var p in e.problems) {
print('Problem: ${p.code}: ${p.msg}');
}
}
}
}
Right now, I can generate an access token using the googleapis_auth package. But I am confused about which email to put in the gmailSaslXoauth2 function as the userEmail parameter. I have two emails, one is my personal account in which I created the project and created a service account and an email generated in json file named as client_email.
client_email looks like this example#example.iam.gserviceaccount.com.
I wish to save the userid string that am getting from a function that parses and decodes JWT token , and be able to use it in other pages in my Flutter app . I tried to save it inside shared preferences but doesn't seem to be working .This is my function and how I used shared preferences
String userName;
dynamic authenticator;
String _decodeBase64(String str) {
String output = str.replaceAll('-', '+').replaceAll('_', '/');
switch (output.length % 4) {
case 0:
break;
case 2:
output += '==';
break;
case 3:
output += '=';
break;
default:
throw Exception('Illegal base64url string!"');
}
return utf8.decode(base64Url.decode(output));
}
String _userid = '';
Map<String, dynamic> parseJwt(String token) {
final parts = token.split('.');
if (parts.length != 3) {
throw Exception('invalid token');
}
final payload = _decodeBase64(parts[1]);
final payloadMap = json.decode(payload);
if (payloadMap is! Map<String, dynamic>) {
throw Exception('invalid payload');
}
print(payload);
addStringToSF() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
_userid = payloadMap['user_id'];
prefs.setString('stringValue',_userid );
}
//print(payloadMap['user_id']);
return payloadMap;
}
getStringValuesSF() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
//Return String
String _userid = prefs.getString('userid');
print (_userid);
return _userid;
}
#override
void initState() {
super.initState();
getStringValuesSF();
}
authenticate() async {
// keyclock url : key-clock-url : example : http://localhost:8080
// my realm : name of your real.m
var uri = Uri.parse('http://169.254.105.22:8080/auth/realms/Clients');
// your client id
var clientId = 'helium';
var scopes = List<String>.of(['openid', 'profile']);
var port = 8080;
var issuer = await Issuer.discover(uri);
var client = new Client(issuer, clientId);
print(issuer.metadata);
urlLauncher(String url) async {
if (await canLaunch(url)) {
await launch(url, forceWebView: true);
} else {
throw 'Could not launch $url';
}
}
authenticator = new Authenticator(
client,
scopes: scopes,
port: port,
urlLancher: urlLauncher,
);
var c = await authenticator.authorize();
closeWebView();
var token = await c.getTokenResponse();
var userInformation = await c.getUserInfo();
setState(() {
userAccessToken = token.accessToken;
userName = userInformation.preferredUsername;
});
//print(token);
//return token;
parseJwt(userAccessToken);
}
I wish to use the userid variable here instead of the static string (id) am passing , in a way it dynamically reads the value from the function then use it inside the link to show the user's info :
final response = await http.get('http://169.254.105.22:8093/user/v1/users/d374169b-c61f-4a5a-b00a-2a2a8d9c4e19');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return User.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load user');
}
}
The second function is in another page (profile page), if anyone knows how I can save the userid from the function , then pass to another page (using sp or any other way) please don't hesitate to help thank you in advance
In my experience, when it comes to simple key/value storage, GetStorage is easier to use and less finicky than Shared Preferences. Try this:
Put this in your main before running your app.
await GetStorage.init();
Then your addStringToSF method would look like this:
addStringToSF() async {
final box = GetStorage();
_userid = payloadMap['user_id'];
box.write('stringValue', _userid);
}
Then from anywhere in your app access the stored value by
final box = GetStorage();
final newString = box.read('stringValue');
That should work for you.