Class OidcRequestAuthorizer

java.lang.Object
com.codename1.io.oidc.OidcRequestAuthorizer
All Implemented Interfaces:
RequestAuthorizer, RequestAuthorizer.Proactive

public final class OidcRequestAuthorizer extends Object implements RequestAuthorizer.Proactive

Sends an OidcClient's access token with the application's requests, and renews it with the refresh token when the service refuses it.

OidcRequestAuthorizer authorizer = new OidcRequestAuthorizer(client);
authorizer.install("https://api.example.com");
authorizer.load();          // a session saved by an earlier run, if there is one

From then on every request under that base URL carries Authorization: Bearer ..., generated @RestClient clients included. The authorizer follows its client: tokens obtained by OidcClient.authorize(), OidcClient.refresh(String) or the device grant are picked up as they arrive, and OidcClient.clearStoredTokens() drops them.

When the service answers 401

The request is held and the refresh token is exchanged for a new set -- once, however many requests were refused together; they all wait for the same exchange. Each is then sent again with the new access token. See RequestAuthorizer for what the caller of a request sees.

When the authorization server returns a permanent refresh error, the session is over: the tokens are dropped from memory and from the TokenStore, the held requests deliver their 401, and every OidcRequestAuthorizer.SignInRequiredListener is told so the application can show its sign-in screen. Transport failures, malformed responses, and the provider's server_error or temporarily_unavailable errors keep the tokens: nothing has said they are bad.

Before the token expires

A refusal is the fallback, not the way a token is normally renewed. When a request is queued and the access token is within setRefreshLeeway(int) of its expiry -- sixty seconds unless set -- the refresh token is exchanged first and the request is kept out of the queue until the exchange is done. It is then sent once, with the new token. Requests queued in the meantime wait for the same exchange.

Nothing blocks for this: the request has simply not been handed to a network thread yet. Code that waits for it -- addToQueueAndWait, the blocking methods of RequestBuilder -- returns the one final answer.

If that exchange is refused the session ends as described above, and the request goes out with no token for the service to answer 401. If it fails without an answer the request is sent with the token it has, which may still be good, and no exchange is tried ahead of time for the next few seconds.

A token whose response carried no expires_in has no known expiry, and is renewed only when the service refuses it.

Threads

Everything an authorizer holds -- the tokens, the exchange in progress, the listeners -- belongs to the event dispatch thread and is read and changed nowhere else. Nothing here is locked. A network thread never calls an authorizer: it sends the header the EDT put on the request when the request was queued. Tokens that arrive on a network thread, and a 401 seen there, are passed to the EDT before the authorizer hears of them.

Call this class on the EDT. The methods that read or change its state can also be called from another thread, and then wait for the EDT to do the work -- so not from a thread the EDT is itself waiting for.

  • Constructor Details

    • OidcRequestAuthorizer

      public OidcRequestAuthorizer(OidcClient client)

      An authorizer for the tokens of client.

      A client has one authorizer: creating a second one for the same client takes its place.

      Parameters
      • client: a configured client
  • Method Details

    • install

      public OidcRequestAuthorizer install(String baseUrl)

      Registers this authorizer for every request under a base URL. Shorthand for NetworkManager.setAuthorizer(String, RequestAuthorizer), whose matching rules apply.

      Parameters
      • baseUrl: the base URL of the application's service
      Returns

      this authorizer

    • load

      public AsyncResource<OidcTokens> load()

      Reads the tokens an earlier run saved into memory. Call it when the application starts; with a SecureStorageTokenStore that requires biometrics, this is the one moment the user is prompted.

      Returns

      a resource that completes with the tokens, or with null when nothing was stored

    • getTokens

      public OidcTokens getTokens()

      The tokens in use.

      Returns

      the tokens, or null when nobody is signed in

    • setTokens

      public void setTokens(OidcTokens tokens)

      Replaces the tokens in use. Tokens the client obtains arrive here by themselves; this is for a set that came from somewhere else.

      Parameters
      • tokens: the tokens, or null for none
    • isSignedIn

      public boolean isSignedIn()
      Whether there is an access token to send.
    • signOut

      public AsyncResource<Boolean> signOut()

      Drops the tokens from memory and from the client's TokenStore. Requests go out without a header from then on. This doesn't tell the server; call OidcClient.revoke(String) with the refresh token first for that.

      A renewal in progress is abandoned: whatever it comes back with is dropped, and the requests waiting for it go out with no token.

      Returns

      the result of clearing the store

    • addSignInRequiredListener

      public void addSignInRequiredListener(OidcRequestAuthorizer.SignInRequiredListener listener)
      Adds a listener told when the session can't be renewed.
    • removeSignInRequiredListener

      public void removeSignInRequiredListener(OidcRequestAuthorizer.SignInRequiredListener listener)
      Removes a listener.
    • setRefreshLeeway

      public OidcRequestAuthorizer setRefreshLeeway(int seconds)

      How close to its expiry an access token is renewed before a request is sent with it. Sixty seconds unless set: long enough for the request to reach a service whose clock runs a little ahead. Zero renews a token only once it has expired, and a negative value turns renewing ahead of time off, leaving the 401 as the only trigger.

      Parameters
      • seconds: the leeway in seconds
      Returns

      this authorizer

    • getRefreshLeeway

      public int getRefreshLeeway()
      The leeway set with setRefreshLeeway(int).
    • getAuthorization

      public String getAuthorization(ConnectionRequest request)
      Called on the event dispatch thread as a request is queued; the request carries the answer to the network thread.
      Specified by:
      getAuthorization in interface RequestAuthorizer
    • prepareAuthorization

      public AsyncResource<Boolean> prepareAuthorization(ConnectionRequest request)
      Called on the event dispatch thread as a request is queued.
      Specified by:
      prepareAuthorization in interface RequestAuthorizer.Proactive
    • refreshAuthorization

      public AsyncResource<Boolean> refreshAuthorization(ConnectionRequest request, String rejectedAuthorization)
      Description copied from interface: RequestAuthorizer

      Called after the service answered 401 to a request that carried this authorizer's header. Called on the event dispatch thread, at most once per request.

      Several requests can be refused at the same moment. An implementation should renew its credential once and give every one of them the same answer, and should recognize a rejectedAuthorization that is no longer the current one: that request was sent before an earlier renewal finished, and only needs sending again.

      Parameters
      • request: the request that was refused

      • rejectedAuthorization: the header value the service refused

      Returns

      a resource that completes with true once a different credential is ready, and with false or an error when there is none to be had. Null means the same as false

      Specified by:
      refreshAuthorization in interface RequestAuthorizer