Keycloak Flutter는 Flutter 애플리케이션용 클라이언트 어댑터로, 오픈소스 기반의 아이덴티티 및 접근 관리 솔루션인 Keycloak과의 통합을 가능하게 합니다. keycloak-js 구현을 기반으로 하여 Flutter 앱에 인증 및 권한 부여 기능을 제공하며, 로그인, 로그아웃, 토큰 관리, 역할 기반 접근 제어 등의 기능을 지원합니다. 이 패키지는 Keycloak의 강력한 보안 프레임워크를 활용하여 모바일 및 웹 환경에서 사용자 인증을 안전하게 간편화합니다.
사용 사례
Flutter 앱에서의 안전한 사용자 인증
기존 Keycloak 서버와의 통합
모바일 앱에서의 역할 기반 접근 제어
다중 서비스 간 단일 로그인(Single sign-on, SSO)
하이브리드 애플리케이션에서 사용자 세션 관리
주요 기능
Keycloak 서버를 통한 인증
토큰 갱신 및 저장
SSO 지원
역할 및 권한 확인
경량이며 keycloak-js와 호환됩니다
적합한 대상
보안 로그인을 필요로 하는 모바일 앱
Keycloak를 사용하는 기업용 애플리케이션
SSO 통합이 필요한 앱
Keycloak에 익숙한 개발자
신원 관리를 우선시하는 프로젝트
고려 사항
실행 중인 Keycloak 서버가 필요합니다
제한된 플랫폼 지원 (명시적인 플랫폼이 나와 있지 않음)
올바른 Keycloak 구성에 따라 달라집니다
내장된 UI 컴포넌트 없음
토큰 만료 및 갱신의 수동 처리
원본 README
연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.
Keycloak Flutter
Easy Keycloak setup for Flutter applications.
About
This library helps you to
use keycloak-js in Flutter
applications providing the following features:
A Keycloak Service which wraps the keycloak-js methods to be used in Flutter, giving extra
functionalities to the original functions and adding new methods to make it easier to be consumed by
Flutter applications.
Generic AuthGuard implementation, so you can customize your own AuthGuard logic inheriting the authentication
logic and the roles load. (coming soon)
A HttpClient interceptor that adds the authorization header to all HttpClient requests.It is also possible to disable this interceptor or exclude routes from having the authorization header. (coming
soon)
This documentation also assists you to configure the keycloak in your Flutter applications and with
the client setup in the admin console of your keycloak installation. (coming soon)
Compatibility
The table below shows the compatibility of keycloak flutter with keycloak. Note that this table will be updated and is
not set in stone
Keycloak_flutter version
Keycloak version
v0.0.3
v10 - v13
v0.0.19
v17 - v19
v0.0.20 - latest
v20 - latest
Installation
Firstly, you need to have keycloak configured. Duh!
Include keycloak_flutter as a dependency in the
dependencies section of your pubspec.yaml file :
Next, In your web/index.html, you need to add a script with a source that references your keycloak.js file. You can
find v10.0.2 in the example project. Your head tag should look as below.
<!DOCTYPE html><html><head><basehref="/"><!-- CODE REMOVED FOR BREVITY --><linkrel="manifest"href="manifest.json"><scriptsrc="js/keycloak.js"></script></head><body><script>if ('serviceWorker'in navigator) {
window.addEventListener('flutter-first-frame', function () {
navigator.serviceWorker.register('flutter_service_worker.js');
});
}
</script><scriptsrc="main.dart.js"type="application/javascript"></script></body></html>
Now, create a frontend client as you usually would in keycloak. Ensure you set your web origins configuration properly.
If you are still having issues, you can import the client included in the
example application
Choosing the right keycloak-js version
The Keycloak client documentation recommends to use the same version of your Keycloak installation.
A best practice is to load the JavaScript adapter directly from Keycloak Server as it will automatically be updated
when you upgrade the server. If you copy the adapter to your web application instead, make sure you upgrade the
adapter
only after you have upgraded the server.
You can now use keycloak in your app.
Note
You need to ensure you do not create multiple instances of keycloak. The example below uses a provider to ensure this.
Use the code provided below as an example and implement it's functionality in your application. In this process ensure
that the configuration you are providing matches that of your client as configured in Keycloak.
Please check and run the example code included in this repository.
A sample keycloak client is also included in the example codebase
lateKeycloakService keycloakService;
voidmain() async {
keycloakService = KeycloakService(KeycloakConfig(
url: 'http://localhost:8080', // Keycloak auth base url
realm: 'sample',
clientId: 'sample-flutter'));
keycloakService.init(
initOptions: KeycloakInitOptions(
onLoad: 'check-sso',
responseMode: 'query',
silentCheckSsoRedirectUri:
'${window.location.origin}/silent-check-sso.html',
),
);
runApp(MyApp());
}
classMyAppextendsStatelessWidget{
// This widget is the root of your application.@overrideWidgetbuild(BuildContext context) {
returnMaterialApp.router(
title: 'Keycloak Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
routerConfig: _router,
);
}
}
classMyHomePageextendsStatefulWidget{
MyHomePage({Key? key, this.title}) : super(key: key);
finalString? title;
@override_MyHomePageStatecreateState() => _MyHomePageState();
}
class_MyHomePageStateextendsState<MyHomePage> {
KeycloakProfile? _keycloakProfile;
void_login() {
keycloakService.login(KeycloakLoginOptions(
redirectUri: '${window.location.origin}',
));
}
@overridevoidinitState() {
// TODO: implement initStatesuper.initState();
try {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
keycloakService.keycloakEventsStream.listen((event) async {
print(event);
if (event.type == KeycloakEventType.onAuthSuccess) {
_keycloakProfile = await keycloakService.loadUserProfile();
} else {
_keycloakProfile = null;
}
setState(() {});
});
if (keycloakService.authenticated) {
_keycloakProfile = await keycloakService.loadUserProfile(false);
}
setState(() {});
});
} catch (e) {
print(e);
}
}
@overrideWidgetbuild(BuildContext context) {
// This method is rerun every time setState is called, for instance as done// by the _incrementCounter method above.//// The Flutter framework has been optimized to make rerunning build methods// fast, so that you can just rebuild anything that needs updating rather// than having to individually change instances of widgets.returnScaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by// the App.build method, and use it to set our appbar title.
title: Text('Sample'),
actions: [
IconButton(
icon: Icon(Icons.logout),
onPressed: () async {
await keycloakService.logout();
}),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Welcome ${_keycloakProfile?.username ?? 'Guest'}',
style: Theme
.of(context)
.textTheme
.headline4,
),
SizedBox(
height: 20,
),
if (_keycloakProfile?.username == null)
ElevatedButton(
onPressed: _login,
child: Text(
'Login',
style: Theme
.of(context)
.textTheme
.headline4,
),
),
SizedBox(
height: 20,
),
if (_keycloakProfile?.username != null)
ElevatedButton(
onPressed: () async {
print('refreshing token');
await keycloakService.updateToken(1000).then((value) {
print(value);
}).catchError((onError) {
print(onError);
});
},
child: Text(
'Refresh token',
style: Theme
.of(context)
.textTheme
.headline4,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _login,
tooltip: 'Login',
child: Icon(Icons.login),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
final _router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => MyHomePage(),
),
],
);
In the example we have set up Keycloak to use a silent check-sso. With this feature enabled, your browser will not do
a full redirect to the Keycloak server and back to your application, instead this action will be performed in a hidden
iframe, so your application resources only need to be loaded and parsed once by the browser when the app is initialized
and not again after the redirect back from Keycloak to your app.
To ensure that Keycloak can communicate through the iframe you will have to serve a static HTML asset from your
application at the location provided in silentCheckSsoRedirectUri.
Create a file called silent-check-sso.html in the assets directory of your application and paste in the contents as
seen below.
Please check the example code included in this repository
If you want to know more about these options and various other capabilities of the Keycloak client is recommended to
read the JavaScript Adapter documentation.
FAQ
Q: Why am I getting 'Promise was rejected with a value of undefined'?
A: Ensure you have setup your frontend client properly, specifically the web origins configuration. This issue usually
occurs when the login status iframe cannot check for the login status of the user because of wrong web origins config.
Please check the included client in the example app.
Q: Do I have to use go_router like the example app??
A: No. You absolutely do not. However, you need to use a router that supports and recognises url query parameters or url
fragments because this is how keycloak communicates the login state of your application. Below are some that will work.