openid_client
openid_client copied to clipboard
Refresh Token automatically after it expires to keep the user session active
Hello everyone , am using the openeiclient package in my flutter app to redirect my users to Keycloak so they ken log in . My method works perfectly fine and am able to retrieve the JWT token , I want to be able to refresh the token am getting whenever it expires so I can keep my user session active and I have no idea how to do it .This is the function am using to redirect me to Keycloak :
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;
userRefreshToken = token.refreshToken;
print (userRefreshToken);
userName = userInformation.preferredUsername;
});
//print(token);
//return token;
parseJwt(userAccessToken);
}
If you know how I can check if my token has expired then automatically ask for a new token (which include new accessToken and a new refreshToken ) please guide me through this for I've been stuck for a while .Thank u in advance
I tried as a normal http request I think it might work try this "https://YOUR_URL/auth/realms/REALNAME/protocol/openid-connect/token".
you need to send the data in x-www-form-urlencoded
body:
client_id: yourClientID grant_type: "refresh_token" refresh_token: YOURTOKEN client_secret: YOURSECRET
have you found a way for a logout ? revoke function is not working for me it sends an exception
@khamisEDX Thank you for your feedback , can u please share with me the whole code am a bit confused as to where I should put the http request and what x-www-form-urlencoded means please if you have a full example can u share it with me ? as for the logout function am still not implementing one
@talbiislam96
Thank u , what am looking for though is a function in flutter that can do this
@talbiislam96 you can still do it through an http request post method will do ?
I know but I want to be able to do it automatically whenever it expires
@khamisEDX I don't think we can use client secret in flutter mobile app it's not safe at all
var token = await c.getTokenResponse();
automatically refreshes the token if it's expired when its called. If you want a new one call it again. You can also use c.createHttpClient()
and using that as your Client
for your network request will automatically refresh the token too.
This package currently does not support persisting state or proactively refresh the token. It is up to the user of this package to implement this functionality. It should be quite straightforward with the getTokenResponse
and other existing functionalities.
If you believe, this functionality should be part of this package, feel free to create a PR.
I Implemented it like below. Eventually Auth.login() gets called by a button in the application.
main.dart: Place where credential is stored for lifetime of application
var cred;
Future main() async {
runApp(MyApp());
}
Authentication Service: Sets the credential
class Auth {
static const String clientId = 'my-client-id';
static const List<String> scopes = ['profile offline_access'];
Future login(BuildContext context) async {
Uri uri = Uri.parse("http://my-auth-service-url");
var issuer = await Issuer.discover(uri);
var client = Client(issuer, clientId);
// create a function to open a browser with an url
urlLauncher(String uri) async {
var u = Uri.parse(uri);
if (await canLaunchUrl(u)) {
await launchUrl(u);
} else {
throw 'Could not launch $uri';
}
}
// create an authenticator
var authenticator = Authenticator(client,
scopes: scopes, port: 4000, urlLancher: urlLauncher);
// starts the authentication
var credential = await authenticator.authorize();
main.cred = credential;
// close the webview when finished
closeInAppWebView();
....
rest omitted for brevity
....
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => const Home(title: "")));
}
Authentication Interceptor: Uses the credential to get token and refresh when necessary
class AuthenticationInterceptor implements RequestInterceptor {
@override
FutureOr<Request> onRequest(Request request) async {
var credential = main.cred as Credential;
var token = (await credential.getTokenResponse()).accessToken;
try {
request.headers["Authorization"] = 'Bearer $token';
request.headers["Accept"] = 'application/json';
request.headers["content-type"] = 'application/json';
} catch (e) {
// TODO: Handle Error here
}
return request;
}
}