Skip to content

package:oidc_web_core

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

dart pub add oidc_web_core oidc_core

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:

redirect.html

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 the redirect_uri doesn't actually serve this page. Make sure your host or reverse proxy serves redirect.html at the exact redirect_uri path 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

  1. 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'),
        ),
    );
    
  2. Init the manager
    await manager.init();
    
  3. Access the user
    manager.userChanges().listen((user) {});
    
    or
    manager.currentUser;
    
  4. Login
    final user = await manager.loginAuthorizationCodeFlow();
    
  5. Logout
    await manager.logout();