An alternative to package:oidc for dart web apps, that does NOT depend on flutter.
This can be used in things like ngdart.
The package uses package:web to access browser APIs, making it also WASM compatible.
Learn more about developing dart web apps in: dart.dev/web
Getting started
The setup here is similar to what is done in package:oidc
Add the dependencies
Add redirect.html page
You need a separate html page to be delivered with your app, which will handle oidc-related requests.
you can get the page from the example project:
it doesn't matter where you put the page and what you call it, but it MUST be delivered from your redirect_uri
for example, here is a common configuration using this page:
final htmlPageLinkDevelopment = Uri.parse('http://127.0.0.1:22433/redirect.html');
final htmlPageLinkProduction = Uri.parse('https://mywebsite.com/redirect.html');
final htmlPageLink = kDebugMode ? htmlPageLinkDevelopment : htmlPageLinkProduction;
final redirectUri = htmlPageLink;
final postLogoutRedirectUri = htmlPageLink;
final frontChannelLogoutUri = htmlPageLink.replace(
queryParameters: {
...htmlPageLink.queryParameters,
'requestType': 'front-channel-logout'
}
);
Note how frontChannelLogoutUri needs requestType=front-channel-logout for the page to know the request type.
you will have to register these urls with the openid provider first, depending on your configuration.
also the html page is completely customizable, but it's preferred to leave the javascript part as is, since it's well-integrated with the plugin. All user-visible copy lives in a single messages object near the top of the script and is safe to translate.
How the page talks to the app
The page and the app exchange messages over a BroadcastChannel:
- on load, the page posts the incoming redirect to the app as a small JSON envelope:
{"v":2,"type":"redirect","uri":"<full redirect url>"}; - once the app has processed it (the token exchange finished โ or failed), the app posts an acknowledgement back:
{"v":2,"type":"ack","status":"ok"|"error","message":"<short message>"}.
The page only shows a success state after an ok ack โ so it no longer claims "Operation Successful" when the app-side exchange actually failed. It shows an error state on an error ack, or when the provider returned an error directly in the redirect (error/error_description/error_uri, per RFC 6749 ยง4.1.2.1). If no ack arrives within ~10s (e.g. an older app version that never acks), or the browser refuses to close a tab the script didn't open (email-link tabs), it falls back to a neutral "you can now close this tab" state.
The app still accepts the older bare-URL message, so an outdated copy of the page keeps working after you upgrade the package. The reverse does not hold: this v2 page needs the matching (v2-aware) package version.
Upgrading
redirect.html is copied into your own project, not shipped by the package, so dart pub upgrade never updates it. When you upgrade oidc_web_core, re-copy the page from the example so the app and the page stay on the same wire version.
Troubleshooting
- The page never loads (blank tab, or your host/proxy's
502/504). None of the javascript above can run if theredirect_uridoesn't actually serve this page. Make sure your host or reverse proxy servesredirect.htmlat the exactredirect_uripath in every environment. While that tab is broken the app receives nothing, so the pending login stays unresolved โ return to the app to cancel it (or let it time out) and retry once the page is served correctly.
Usage
using this package is identical to package:oidc
you can also see an example here of a dart web app using this package
- Define the manager
final manager = OidcUserManagerWeb.lazy( discoveryDocumentUri: OidcUtils.getOpenIdConfigWellKnownUri( Uri.parse('https://demo.duendesoftware.com'), ), // this is a public client, // so we use [OidcClientAuthentication.none] constructor. clientCredentials: const OidcClientAuthentication.none( clientId: 'interactive.public.short', ), // Use a web-only store store: const OidcWebStore(), settings: OidcUserManagerSettings( frontChannelLogoutUri: Uri(path: 'redirect.html'), uiLocales: ['en', 'ar'], refreshBefore: (token) { return const Duration(seconds: 1); }, // scopes supported by the provider and needed by the client. scope: ['openid', 'profile', 'email', 'offline_access'], // this url must be an actual html page. // see the file in /web/redirect.html for an example. // // for debugging in flutter, you must run this app with --web-port 22433 postLogoutRedirectUri: Uri.parse('http://127.0.0.1:22433/redirect.html'), redirectUri: Uri.parse('http://127.0.0.1:22433/redirect.html'), ), ); - Init the manager
- Access the user or
- Login
- Logout