Flutter - Keycloak integration with openId ( redirecting issue ) - flutter

I want to authenticate flutter app with Keycloak service via internal webview (without open web browser)
To achieve this objective I used OpenID
When app runs will appear Keycloak login page in internal webview. But when entering the username and password correctly, it redirects to another web page which is as follows.
I guess this case happen due to flutter app cannot handle custom redirections. Does anyone know how to fix this??
My code:
urlLauncher(String url) async {
if (await canLaunch(url)) {
await launchUrlString(url, mode: LaunchMode.inAppWebView);
} else {
print("TOKEN = error");
throw 'Could not launch $url';
}
}
// create an authenticator
var authenticator = new Authenticator(
client,
redirectUri: Uri.parse(_redirectUrl),
scopes: scopes,
urlLancher: urlLauncher,
);
// starts the authentication
var c = await authenticator.authorize();
print("TOKEN = DONE");
// close the webview when finished
await closeInAppWebView();
var res = await c.getTokenResponse();
print("TOKEN = ${res.accessToken}");
pubspec.yaml:
openid_client: ^0.4.6
url_launcher: ^6.1.6
I tried open id , simple auth , flutter app auth also, unfortunately flutter app auth cannot use with internal webview

Future<TokenResponse> authenticate(Uri uri, String clientId,
List<String> scopes, BuildContext context) async {
try {
var issuer = await Issuer.discover(uri);
var client = Client(issuer, clientId);
urlLauncher(String url) async {
Uri uri = Uri.parse(url);
if (await launchUrl(uri)) {
} else {
throw 'Could not launch $url';
}
}
var authenticator = Authenticator(
client,
scopes: scopes,
urlLancher: urlLauncher,
port: 3000,
);
var c = await authenticator.authorize();
await closeInAppWebView();
var res = await c.getTokenResponse();
UserInfo use = await c.getUserInfo();
authUserId(use.subject.toString());
email(use.email.toString());
token(res.accessToken.toString());
logoutUrl = c.generateLogoutUrl();
return res;
} finally {
context.loaderOverlay.hide();
}
}
You can try this out.
Saved the logout URL into a variable cos it'll be needed to logout.

Related

Why is my flutter app not handling Spotify's API authorization after signing in?

I'm making a flutter app using Spotify's API. I have a basic homepage that uses a button to launch a browser to login to Spotify. Here is my backend code:
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:uni_links/uni_links.dart';
class SpotifyAuth with ChangeNotifier {
final String CLIENT_ID = "My client ID";
final String ClIENT_SECRET = "My client secret";
final String REDIRECT_URI = "http://localhost:8000/callback";
final String SCOPE = 'user-read-private user-read-email';
// var state = 'your-state';
late String _accessToken;
late String _refreshToken;
Uri createAuthenticationUri(){
var query = [
'response_type=code',
'client_id=$CLIENT_ID',
'scope=${Uri.encodeComponent(SCOPE)}',
'redirect_uri=${Uri.encodeComponent(REDIRECT_URI)}',
];
var queryString = query.join('&');
var url = 'https://accounts.spotify.com/authorize?' + queryString;
var parsedUrl = Uri.parse(url);
return parsedUrl;
}
Future<void> launchInBrowser() async {
if (!await launchUrl(
createAuthenticationUri(),
mode: LaunchMode.externalApplication,
)){
throw Exception('Could not launch Url');
}
}
Future<void> launchAuth() async {
await launchInBrowser();
await initUniLinks();
}
Future<void> getAccessToken(String code) async {
var body = {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": REDIRECT_URI,
"client_id": CLIENT_ID,
"client_secret": ClIENT_SECRET
};
// Create a request header with the required information
var header = {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization":
"Basic ${base64Encode(utf8.encode("$CLIENT_ID:$ClIENT_SECRET>"))}"
};
// Send the request to the Spotify token endpoint
var response = await http.post(
Uri.parse("https://accounts.spotify.com/api/token"),
body: body,
headers: header);
// Check if the request was successful
if (response.statusCode == 200) {
// Parse the JSON response
var data = json.decode(response.body);
// Get the access token from the response
String accessToken = data["access_token"];
// Store the access token for future use
// ...
_accessToken = accessToken;
} else {
print("Error");
}
}
Future<void> initUniLinks() async {
// Get the latest initial link
String? initialLink = await getInitialLink();
// Check if the link contains a Spotify authorization code
if (initialLink != null && initialLink.contains("code=")) {
// Extract the code from the link
String code = initialLink.split("code=")[1];
// Use the code to get an access token from Spotify
getAccessToken(code);
}
else{
print("Nothing");
}
}
}
My redirect URI is set in the spotify dashboard.
My app widget calls luanchAuth();
and then it should wait for the authentication code with initUniLinks() but it seems like initUniLinks() executes immediately without waiting for the authentication. When I authenticate in Spotify, it throws a generic "can't connect to localhost" error page but the url includes the auth code that I need.
Not sure what I'm doing wrong here. Admittedly I'm new to Oauth and app-api-connections in general but I thought this would work.
REDIRECT URI is the problem here, You cannot have redirect URI with localhost it fails. either use ngrok and provide the mapped https url or host your callback url and provide it.
Use the custom scheme for redirect_uri, something like this my-app://token/callback. See App Settings for Spotify rules.
Then configure the application for Deep Linking to receive the authentication response.

Why can't I see a cookie I sent from Flask to Flutter in the browser?

I am creating a Flutter Web app that requires login verification. The user makes a post request with authentication information and then my Flask app with send a cookie back to the client.
Here is the code for the Flask App
#app.route('/test', methods=['POST'])
#cross_origin(supports_credentials=True)
def test():
resp = jsonify({'message' : 'Logged in!'})
resp.set_cookie('Set-Cookie', "token", httponly = True, secure = False)
return resp
Here is the Dart/Flutter code where I make the POST request and expect a cookie called 'Set-Cookie'.
class HttpService {
static var dio = Dio();
static testMethod() async {
try {
dio.options.extra['withCredentials'] = true;
var response = await dio.post('http://127.0.0.1:5000/test');
print(response);
} catch (e) {
print(e);
}
}
As you can see, I don't receive this cookie on my browser, but the request is successful and I get the JSON message!
BUT, when I make this same request on Postman, I get the JSON response AND the cookie.
Any help would be greatly appreciated! Let me know if you need any more details/code.
Thanks to Kris, I realized I was making the request from Flutter (Client) to an IP rather than the domain name localhost. Because setting a cookie is domain specific, I couldn't see the cookie set in the developer console.
Here is the updated code
static testMethod() async {
try {
dio.options.extra['withCredentials'] = true;
var response = await dio.post('http://localhost:5000/test');
print(response);
} catch (e) {
print(e);
}
}

Google Auth Page always shown, how to Auth only first time

I am making a calendar app with flutter using googleApi library.
but, When you turn off the app, need to auth again in web site.
i want auth only first time.
is it possible?
// mycode
get _SCOPES => [CalendarApi.CalendarScope];
await clientViaUserConsent(_clientID, _SCOPES, prompt)
.then((AuthClient client) async {
CalendarClient.calendar = CalendarApi(client);
calendarId = await CalendarClient.calendar.calendarList
.list()
.then((value) => value.items[0].id);
});
void saveData(AccessCredentials credentials) {
GetStorage().write(credetialKey, {
"accessTokenData": credentials.accessToken.data,
"accessTokenExpiry": credentials.accessToken.expiry.toString(),
"refreshToken": credentials.refreshToken,
"scopes": credentials.scopes,
"idToken": credentials.idToken
});
}
AccessCredentials getCredetial() {
try {
var map = GetStorage().read(credetialKey);
return AccessCredentials(
AccessToken("Bearer", map["accessTokenData"] as String,
DateTime.parse(map["accessTokenExpiry"])),
map["refreshToken"],
map["scopes"].cast<String>(),
idToken: map["idToken"] as String);
} catch (e) {
return null;
}
}
Client cli = Client();
var c = await refreshCredentials(_clientID, getCredetial(), cli)
.catchError((e) {
print(e);
});
authenticatedClient(cli, c);
error :
DetailedApiRequestError(status: 401, message: Request is missing required authentication credential. Expected OAuth 2 access tok
You can save user session using for example sharedPreferences. Each time the user launch the app your must first check if the session is saved so you can skip the auth process, otherwise you initiate the authentication
i solved it.
save AccessCredentials,
and use autoRefreshingClient;
Client cli = Client();
var c = await refreshCredentials(_clientID, getCredetial(), cli)
.catchError((e) {
print(e);
});
cli = autoRefreshingClient(_clientID, c, cli);

How can I solve this Access was denied (www-authenticate header was: Bearer realm="https://accounts.google.com/", error=invalid_token)

In early, my app is fine but few hour later I get this error.
My follow is login to google drive and show that file in my app.
I/flutter (12843):
Access was denied (www-authenticate header was: Bearer realm="https://accounts.google.com/", error=invalid_token).
here is my code.
Future<http.Client> getHttpClient() async {
//Get Credentials
var credentials = await storage.getCredential();
if (credentials == null) {
//Needs user authentication
var authClient = await clientViaUserConsent(
ClientId(_clientId, _clientSecret), _scopes, (url) {
//Open Url in Browser
launch(url);
});
//Save Credentials
await storage.saveCredentials(authClient.credentials.accessToken,
authClient.credentials.refreshToken);
return authClient;
} else {
//Already authenticated
return authenticatedClient(
http.Client(),
AccessCredentials(
AccessToken(credentials["type"], credentials["data"],
DateTime.tryParse(credentials["expiry"])),
credentials["refreshToken"],
_scopes));
}
}

Auth0-How to use with Flutter

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.