Classes

The following classes are available globally.

  • To test AuthWebViewController without any actual provider.

    1. It spends some time in the “preparing” state sometimes skipping it.
    2. Then pretends that it got an endpoint (an inline page) with a link to a redirect URL. Tapping the link should cause the web view to intercept it and notify the view model via handleRedirect().
    3. The latter is going to jump to a “finalizing” state spending some time there (possibly skipping it) and then jumping to “completed”. And of course it can randomly fail at any step.
    See more
  • A basic in-app browser to use with OAuth/OpenID flows. Nothing special here, you can make your own or direct the user to an external browser, for example.

    See more
  • Basic OAuth 2.0 client supporting OpenID as well.

    OAuth Refresher

    1. The app (“client”) wants to get access to protected resources of the end-user and thus needs a permission from them in the form of an access token, a string that the app is going to show to the server storing those resources.

    2. In order to obtain the access token the app opens a web browser (in-app or external) and navigates it to the corresponding “authorization server”. It tells the server what kind of app it is (“client identifier”), what resources the talk is about (“scope”), and how the credentials should be returned back (“response type”).

    3. The end-user authenticates themselves on this page and confirms that they are OK with the app accessing the resources in question.

    4. The server responds back to the app by directing the browser to a “redirect URL” mentioned by the app earlier adding its response in “query” or “fragment” parts of this URL.

    5. The app then either directly finds the access token in this response URL (“implicit flow”) or uses a short-lived code from there to get the access token via a separate network call to the “token endpoint” (“authorization code flow”).

    Note that the extra step about code seems like having no sense for a native app. This is because it was designed for web servers and similar clients allowing them to obtain access tokens without the end-user (and thus any malicious code running on their device) seeing them. Note also that the access tokens usually expire but might be refreshed using a “refresh token”. A quirk of the protocol is that this token, if available, can be obtained only via “authorization code flow”.

    OpenID Refresher

    OpenID is essentially an OAuth flow where the resource being accessed is some basic information about the user. Implementation-wise the “scope” of such a flow includes “openid” and a more fancy JWT ID Token is returned in addition to (or instead of) the usual access token. It’s an official replacement for ad-hoc OAuth flows used earlier to authenticate users of one service by asking them via OAuth for access to their email address or basic details let say on another service.

    Usage

    TLDR: call start*() and watch the state to become .authorized helping with authorization UI when needed.

    Nothing happens when the client is initialized, i.e. it stays at .idle.

    Once start() and friends are called the client checks the storage for the existing credentials corresponding to the passed scope and response types:

    • If credentials were found and are not expired or can be refreshed, then it directly switches to .authorized state. The user code now has access to the token(s).

    • If no good credentials were found and a non-interactive (“silent”) mode was specified, then the client cancels the flow. The user code can treat this state as “not logged in” / “have no access”.

    • If no good credentials were found and an interactive mode was specified, then the client enters .authorizing state and expects the user code to help with authorization UI by presenting a browser (in-app or external).

    (See AuthWebViewController in “MMMoth/UI” for a basic implementation.)

    The browser should navigate to the endpoint associated with .authorizing state. The user code should be able to intercept all the requests to the associated redirect URL and feed them back to the client via handleAuthorizationRedirect(); it should also report any errors opening the endpoint via handleAuthorizationFailure() and can cancel the flow via cancel().

    When the client gets information from authorization server via handleAuthorizationRedirect(), then it either:

    • immediately fails the flow (in case the server returned an explicit error or provided an invalid response);

    • or directly enters .authorized state (in case of an “implicit” flow, that is when responseType does NOT include .code);

    • or begins exchanging the authorization code to token(s) (in case of an “authorization code” flow, that is when responseType does include .code).

    A picture for the above:

             ┌─────────────────┐
                   idle       
             └─────────────────┘
                      
                      
                                             ┏━━━━━━━━━━━━━━━━━┓
                  start()  ─────────────────▶┃   authorized    
                            Have credentials ┗━━━━━━━━━━━━━━━━━┛
                             in the storage
                      
            No good credentials              ┌─────────────────┐
               in the storage   ────────────▶│    cancelled    
                                Silent mode └─────────────────┘
                      
     Interactive mode 
                      
                        The user code opens a browser
                 authorizing      and directs it to the
                        specified URL.
    
    
     The user code managing the browser calls either of these:
    
                                             ┌─────────────────┐
                                   cancel()─▶│    cancelled    
                                             └─────────────────┘
                                             ┌─────────────────┐
               handleAuthorizationFailure()─▶│     failed      
                                             └─────────────────┘
                                                      
              handleAuthorizationRedirect()           
    
     Implicit    Auth Code                         
         flow    or Hybrid 
                      flow                    
                           
                     fetchingToken           
                           
                           
                           
                  ┏━━━━━━━━━━━━━━━━━┓
              └───▶┃   authorized    
                   ┗━━━━━━━━━━━━━━━━━┛
    

    Notes

    Initially I wanted to have an OpenID client that would be using an OAuth client under the hood, but this would be more complicated without the OAuth client knowing of OpenID-specific parameters.

    See more
  • OpenID config that’s immediately available.

    See more
  • OpenID config that can be fetched from a provider according to OpenID Connect Discovery conventions. See [https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig].

    See more
  • Simple parser for ID Tokens, which are non-encrypted JSON Web Tokens.

    In the context of MMMoth library we are only interested in expiration time field, just to know when to refresh the token. We are not concerned with verification, it’s something for the backend accepting the tokens. We don’t want support for generic JWTs either and thus can require some of the fields avoiding optionals.

    See more