Class OidcRequestAuthorizer
- All Implemented Interfaces:
RequestAuthorizer, 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.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic interfaceTold when the user has to sign in again.Nested classes/interfaces inherited from interface RequestAuthorizer
RequestAuthorizer.Proactive -
Field Summary
Fields inherited from interface RequestAuthorizer
NONE -
Constructor Summary
ConstructorsConstructorDescriptionOidcRequestAuthorizer(OidcClient client) An authorizer for the tokens ofclient. -
Method Summary
Modifier and TypeMethodDescriptionvoidAdds a listener told when the session can't be renewed.getAuthorization(ConnectionRequest request) Called on the event dispatch thread as a request is queued; the request carries the answer to the network thread.intThe leeway set withsetRefreshLeeway(int).The tokens in use.Registers this authorizer for every request under a base URL.booleanWhether there is an access token to send.load()Reads the tokens an earlier run saved into memory.prepareAuthorization(ConnectionRequest request) Called on the event dispatch thread as a request is queued.refreshAuthorization(ConnectionRequest request, String rejectedAuthorization) Called after the service answered401to a request that carried this authorizer's header.voidRemoves a listener.setRefreshLeeway(int seconds) How close to its expiry an access token is renewed before a request is sent with it.voidsetTokens(OidcTokens tokens) Replaces the tokens in use.signOut()Drops the tokens from memory and from the client'sTokenStore.
-
Constructor Details
-
OidcRequestAuthorizer
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
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
Reads the tokens an earlier run saved into memory. Call it when the application starts; with a
SecureStorageTokenStorethat 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
The tokens in use.
Returns
the tokens, or null when nobody is signed in
-
setTokens
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
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; callOidcClient.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
Adds a listener told when the session can't be renewed. -
removeSignInRequiredListener
Removes a listener. -
setRefreshLeeway
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
401as the only trigger.Parameters
seconds: the leeway in seconds
Returns
this authorizer
-
getRefreshLeeway
public int getRefreshLeeway()The leeway set withsetRefreshLeeway(int). -
getAuthorization
Called on the event dispatch thread as a request is queued; the request carries the answer to the network thread.- Specified by:
getAuthorizationin interfaceRequestAuthorizer
-
prepareAuthorization
Called on the event dispatch thread as a request is queued.- Specified by:
prepareAuthorizationin interfaceRequestAuthorizer.Proactive
-
refreshAuthorization
public AsyncResource<Boolean> refreshAuthorization(ConnectionRequest request, String rejectedAuthorization) Description copied from interface:RequestAuthorizerCalled after the service answered
401to 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
rejectedAuthorizationthat 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
trueonce a different credential is ready, and withfalseor an error when there is none to be had. Null means the same asfalse- Specified by:
refreshAuthorizationin interfaceRequestAuthorizer
-
-