75 lines
3.6 KiB
Markdown
75 lines
3.6 KiB
Markdown
|
|
# halkyon_oidc_flutter
|
||
|
|
|
||
|
|
The device's half of halkyon_oidc (`halkyon_oidc_server`, `halkyon_oidc_client`),
|
||
|
|
signing in with OpenID Connect for Serverpod v4. It opens the provider's page for an
|
||
|
|
attempt the server began, waits until the browser comes back, and checks that it came
|
||
|
|
back for that attempt. The code it returns goes back to the server, which ends the
|
||
|
|
sign-in. Halkyon signs in with it (ADR 0020, ADR 0023); nothing of Halkyon's is in it.
|
||
|
|
|
||
|
|
## Using it
|
||
|
|
|
||
|
|
```dart
|
||
|
|
final browser = OidcBrowser(loopback: LoopbackWebAuth(appName: 'My App'));
|
||
|
|
|
||
|
|
// The app's endpoint on OidcIdpBaseEndpoint (halkyon_oidc_server's README).
|
||
|
|
final start = await client.signIn.begin(provider: 'keycloak');
|
||
|
|
switch (await browser.open(start, const OidcReturn.scheme('my.app'))) {
|
||
|
|
case null:
|
||
|
|
// The person closed the browser.
|
||
|
|
case OidcDenied(:final error):
|
||
|
|
// The person said no at the provider (`access_denied`).
|
||
|
|
case OidcCode(:final code):
|
||
|
|
final session = await client.signIn.login(attempt: start.attempt, code: code);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
`open` throws an `OidcCallbackException` when the browser came back for another
|
||
|
|
sign-in (`wrongState`), with neither one code nor one refusal (`malformed`), when
|
||
|
|
there's no browser (`noBrowser`), or when the way back failed otherwise (`failed`).
|
||
|
|
|
||
|
|
The state is checked here, on the device: the server keeps the attempt's nonce, which
|
||
|
|
ties the code to the attempt, but never compares the state.
|
||
|
|
|
||
|
|
## The way back
|
||
|
|
|
||
|
|
Pick one per sign-in, before opening the browser: each must match the redirect the
|
||
|
|
server's provider names, or the address its forwarding page sends the browser on to
|
||
|
|
(`addOidcCallbackForwarding`).
|
||
|
|
|
||
|
|
- **`OidcReturn.scheme('my.app')`** on Android, iOS and macOS. The system's browser
|
||
|
|
sheet catches the scheme, even after a redirect: Chrome's Auth Tab (Chrome 132 on),
|
||
|
|
a Custom Tab after the person's click on the consent page in other browsers, and
|
||
|
|
`ASWebAuthenticationSession`. Android needs flutter_web_auth_2's `CallbackActivity`
|
||
|
|
with the scheme, for the browsers without Auth Tab:
|
||
|
|
|
||
|
|
```xml
|
||
|
|
<activity android:name="com.linusu.flutter_web_auth_2.CallbackActivity"
|
||
|
|
android:exported="true" android:taskAffinity="">
|
||
|
|
<intent-filter android:label="flutter_web_auth_2">
|
||
|
|
<action android:name="android.intent.action.VIEW" />
|
||
|
|
<category android:name="android.intent.category.DEFAULT" />
|
||
|
|
<category android:name="android.intent.category.BROWSABLE" />
|
||
|
|
<data android:scheme="my.app" />
|
||
|
|
</intent-filter>
|
||
|
|
</activity>
|
||
|
|
```
|
||
|
|
|
||
|
|
- **`OidcReturn.loopback(port: 43823, path: '/oidc-callback')`** on Linux and Windows:
|
||
|
|
`LoopbackWebAuth` opens the system browser and waits on `127.0.0.1` alone (RFC 8252,
|
||
|
|
7.3), five minutes at most, until `cancel`. It takes the place of flutter_web_auth_2's
|
||
|
|
own, which ends the wait whenever the window regains focus. Give each app its own
|
||
|
|
port.
|
||
|
|
- **`OidcReturn.https(host: 'example.org', path: '/oidc-callback')`** where the system
|
||
|
|
hands the address to the app alone: Android's App Links with Chrome's Auth Tab (the
|
||
|
|
host's `assetlinks.json`), iOS v17.4 and macOS v14.4 on with associated domains
|
||
|
|
(`webcredentials:`). It protects only where nothing else can come back: a hostile app
|
||
|
|
that begins a sign-in of its own may ask for any way back the server still forwards
|
||
|
|
to. On Android it fails where the app isn't verified, as a debug build isn't, and in
|
||
|
|
browsers without Auth Tab (Firefox, as of v156).
|
||
|
|
|
||
|
|
A code caught on its way is worthless without the attempt's ID, which only the device
|
||
|
|
holds, so the scheme and the loopback are safe for the sign-ins the app begins itself.
|
||
|
|
|
||
|
|
## Licence
|
||
|
|
|
||
|
|
BSD-3-Clause, as Serverpod's own (ADR 0023, D2).
|