## The OAuth2 auth provider

This component contains an out of the box OAuth2 implementation.

To use this project, add the following dependency to the dependencies section of your `module.ceylon`:

    shared import io.vertx.ceylon.auth.oauth2 "3.3.2";

OAuth2 lets users grant the access to the desired resources to third party applications, giving them the possibility to enable and disable those accesses whenever they want.

Vert.x OAuth2 supports the following flows.

- Authorization Code Flow (for apps with servers that can store persistent information).

- Password Credentials Flow (when previous flow can’t be used or during development).

- Client Credentials Flow (the client can request an access token using only its client credentials)

### Authorization Code Flow

The authorization code grant type is used to obtain both access tokens and refresh tokens and is optimized for confidential clients. As a redirection-based flow, the client must be capable of interacting with the resource owner’s user-agent (typically a web browser) and capable of receiving incoming requests (via redirection) from the authorization server.

For more details see Oauth2 specification, section 4.1.

### Password Credentials Flow

The resource owner password credentials grant type is suitable in cases where the resource owner has a trust relationship with the client, such as the device operating system or a highly privileged application. The authorization server should take special care when enabling this grant type, and only allow it when other flows are not viable.

The grant type is suitable for clients capable of obtaining the resource owner’s credentials (username and password, typically using an interactive form). It is also used to migrate existing clients using direct authentication schemes such as HTTP Basic or Digest authentication to OAuth by converting the stored credentials to an access token.

For more details see Oauth2 specification, section 4.3.

### Client Credentials Flow

The client can request an access token using only its client credentials (or other supported means of authentication) when the client is requesting access to the protected resources under its control, or those of another resource owner that have been previously arranged with the authorization server (the method of which is beyond the scope of this specification).

The client credentials grant type MUST only be used by confidential clients.

For more details see Oauth2 specification, section 4.4.

### Getting Started

An example on how to use this provider and authenticate with GitHub can be implemented as:

    import io.vertx.ceylon.ext.auth.oauth2 { oAuth2Auth, auth_code }

    ...

    value oauth2 = oAuth2Auth.create(vertx, auth_code, OAuth2ClientOptions {
      clientID = "YOUR_CLIENT_ID";
      clientSecret = "YOUR_CLIENT_SECRET";
      site = "https://github.com/login";
      tokenPath = "/oauth/access_token";
      authorizationPath = "/oauth/authorize";
    });

    // when there is a need to access a protected resource or call a protected method,
    // call the authZ url for a challenge

    value authorization_uri = oauth2.authorizeURL(JsonObject {
      redirect_uri = "http://localhost:8080/callback";
      scope = "notifications";
      state = "3(#0/!~";
    });

    // when working with web application use the above string as a redirect url

    // in this case GitHub will call you back in the callback uri one should now complete the handshake as:

    value code = "xxxxxxxxxxxxxxxxxxxxxxxx";

    oauth2.getToken(JsonObject {
      code = code;
      redirect_uri = "http://localhost:8080/callback";
    }, (AccessToken|Throwable res) {
      if (is Throwable name) {
        // error, the code provided is not valid
      } else {
        // save the token and continue...
      };
    });

#### Authorization Code flow

The Authorization Code flow is made up from two parts. At first your application asks to the user the permission to access their data. If the user approves the OAuth2 server sends to the client an authorization code. In the second part, the client POST the authorization code along with its client secret to the authority server in order to get the access token.

    import io.vertx.ceylon.ext.auth.oauth2 { oAuth2Auth, auth_code }

    ...

    // Set the client credentials and the OAuth2 server
    value credentials = OAuth2ClientOptions {
      clientID = "<client-id>";
      clientSecret = "<client-secret>";
      site = "https://api.oauth.com";
    };

    // Initialize the OAuth2 Library
    value oauth2 = oAuth2Auth.create(vertx, auth_code, credentials);

    // Authorization oauth2 URI
    value authorization_uri = oauth2.authorizeURL(JsonObject {
      redirect_uri = "http://localhost:8080/callback";
      scope = "<scope>";
      state = "<state>";
    });

    // Redirect example using Vert.x
    response.putHeader("Location", authorization_uri).setStatusCode(302).end();

    value tokenConfig = JsonObject {
      code = "<code>";
      redirect_uri = "http://localhost:3000/callback";
    };

    // Callbacks
    // Save the access token
    oauth2.getToken(tokenConfig, (AccessToken|Throwable res) {
      if (is Throwable name) {
        print("Access Token Error: ``res.getMessage()``");
      } else {
        // Get the access token object (the authorization code is given from the previous step).
        value token = res;
      };
    });

#### Password Credentials Flow

This flow is suitable when the resource owner has a trust relationship with the client, such as its computer operating system or a highly privileged application. Use this flow only when other flows are not viable or when you need a fast way to test your application.

    import io.vertx.ceylon.ext.auth.oauth2 { oAuth2Auth, password }
    import io.vertx.ceylon.core.http { get }

    ...

    // Initialize the OAuth2 Library
    value oauth2 = oAuth2Auth.create(vertx, password);

    value tokenConfig = JsonObject {
      username = "username";
      password = "password";
    };

    // Callbacks
    // Save the access token
    oauth2.getToken(tokenConfig, (AccessToken|Throwable res) {
      if (is Throwable name) {
        print("Access Token Error: ``res.getMessage()``");
      } else {
        // Get the access token object (the authorization code is given from the previous step).
        value token = res;

        oauth2.api(get, "/users", JsonObject {
          access_token = token.principal()get("access_token";
        }, (JsonObject|Throwable res2) {
          // the user object should be returned here...
        });
      };
    });

#### Client Credentials Flow

This flow is suitable when client is requesting access to the protected resources under its control.

    import io.vertx.ceylon.ext.auth.oauth2 { oAuth2Auth, client }

    ...

    // Set the client credentials and the OAuth2 server
    value credentials = OAuth2ClientOptions {
      clientID = "<client-id>";
      clientSecret = "<client-secret>";
      site = "https://api.oauth.com";
    };

    // Initialize the OAuth2 Library
    value oauth2 = oAuth2Auth.create(vertx, client, credentials);

    value tokenConfig = JsonObject();

    // Callbacks
    // Save the access token
    oauth2.getToken(tokenConfig, (AccessToken|Throwable res) {
      if (is Throwable name) {
        print("Access Token Error: ``res.getMessage()``");
      } else {
        // Get the access token object (the authorization code is given from the previous step).
        value token = res;
      };
    });

### AccessToken object

When a token expires we need to refresh it. OAuth2 offers the AccessToken class that add a couple of useful methods to refresh the access token when it is expired.

    // Check if the token is expired. If expired it is refreshed.
    if (token.expired()) {
      // Callbacks
      token.refresh((Throwable? res) {
        if (!exists res) {
          // success
        } else {
          // error handling...
        };
      });
    };

When you’ve done with the token or you want to log out, you can revoke the access token and refresh token.

    // Revoke only the access token
    token.revoke("access_token", (Throwable? res) {
      // Session ended. But the refresh_token is still valid.

      // Revoke the refresh_token
      token.revoke("refresh_token", (Throwable? res1) {
        print("token revoked.");
      });
    });

### Example configuration for common OAuth2 providers

#### Google

    import io.vertx.ceylon.ext.auth.oauth2 { oAuth2Auth, client }

    ...

    // Set the client credentials and the OAuth2 server
    value credentials = OAuth2ClientOptions {
      clientID = "CLIENT_ID";
      clientSecret = "CLIENT_SECRET";
      site = "https://accounts.google.com";
      tokenPath = "https://www.googleapis.com/oauth2/v3/token";
      authorizationPath = "/o/oauth2/auth";
    };

    // Initialize the OAuth2 Library
    value oauth2 = oAuth2Auth.create(vertx, client, credentials);

#### GitHub

    import io.vertx.ceylon.ext.auth.oauth2 { oAuth2Auth, client }

    ...

    // Set the client credentials and the OAuth2 server
    value credentials = OAuth2ClientOptions {
      clientID = "CLIENT_ID";
      clientSecret = "CLIENT_SECRET";
      site = "https://github.com/login";
      tokenPath = "/oauth/access_token";
      authorizationPath = "/oauth/authorize";
    };

    // Initialize the OAuth2 Library
    value oauth2 = oAuth2Auth.create(vertx, client, credentials);

#### Linkedin

    import io.vertx.ceylon.ext.auth.oauth2 { oAuth2Auth, client }

    ...

    // Set the client credentials and the OAuth2 server
    value credentials = OAuth2ClientOptions {
      clientID = "CLIENT_ID";
      clientSecret = "CLIENT_SECRET";
      site = "https://www.linkedin.com";
      authorizationPath = "/uas/oauth2/authorization";
      tokenPath = "/uas/oauth2/accessToken";
    };

    // Initialize the OAuth2 Library
    value oauth2 = oAuth2Auth.create(vertx, client, credentials);

#### Twitter

    import io.vertx.ceylon.ext.auth.oauth2 { oAuth2Auth, client }

    ...

    // Set the client credentials and the OAuth2 server
    value credentials = OAuth2ClientOptions {
      clientID = "CLIENT_ID";
      clientSecret = "CLIENT_SECRET";
      site = "https://api.twitter.com";
      authorizationPath = "/oauth/authorize";
      tokenPath = "/oauth/access_token";
    };

    // Initialize the OAuth2 Library
    value oauth2 = oAuth2Auth.create(vertx, client, credentials);

#### Facebook

    import io.vertx.ceylon.ext.auth.oauth2 { oAuth2Auth, client }

    ...

    // Set the client credentials and the OAuth2 server
    value credentials = OAuth2ClientOptions {
      clientID = "CLIENT_ID";
      clientSecret = "CLIENT_SECRET";
      site = "https://www.facebook.com";
      authorizationPath = "/dialog/oauth";
      tokenPath = "https://graph.facebook.com/oauth/access_token";
    };

    // Initialize the OAuth2 Library
    value oauth2 = oAuth2Auth.create(vertx, client, credentials);

#### JBoss Keycloak

When working with keycloak it will be quite simple to setup the OAuth2 provider, just export the JSON config from the web UI and use it as the OAuth2 config with the helper class `OAuth2ClientOptions`.

    import io.vertx.ceylon.ext.auth.oauth2 { oAuth2Auth, client }

    ...

    // After setting up the application and users in keycloak export
    // the configuration json file from the web interface and load it in your application e.g.:

    value keycloakJson = JsonObject {
      realm = "master";
      realm-public-key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqGQkaBkiZWpUjFOuaabgfXgjzZzfJd0wozrS1czX5qHNKG3P79P/UtZeR3wGN8r15jVYiH42GMINMs7R7iP5Mbm1iImge5p/7/dPmXirKOKOBhjA3hNTiV5BlPDTQyiuuTAUEms5dY4+moswXo5zM4q9DFu6B7979o+v3kX6ZB+k3kNhP08wH82I4eJKoenN/0iCT7ALoG3ysEJf18+HEysSnniLMJr8R1pYF2QRFlqaDv3Mqyp7ipxYkt4ebMCgE7aDzT6OrfpyPowObpdjSMTUXpcwIcH8mIZCWFmyfF675zEeE0e+dHKkL1rPeCI7rr7Bqc5+1DS5YM54fk8xQwIDAQAB";
      auth-server-url = "http://localhost:9000/auth";
      ssl-required = "external";
      resource = "frontend";
      credentials = JsonObject {
        secret = "2fbf5e18-b923-4a83-9657-b4ebd5317f60";
      };
    };

    // Initialize the OAuth2 Library
    value oauth2 = oAuth2Auth.createKeycloak(vertx, client, keycloakJson);

When using this approach the provider has knowledge on how to parse access tokens and extract grants from inside. This information is quite valuable since it allows to do authorization at the API level, for example:

    import io.vertx.ceylon.ext.auth.oauth2 { oAuth2Auth, password }

    ...

    // you can now use this config with the OAuth2 provider like this:
    value keycloakJson = JsonObject {
      realm = "master";
      realm-public-key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqGQkaBkiZWpUjFOuaabgfXgjzZzfJd0wozrS1czX5qHNKG3P79P/UtZeR3wGN8r15jVYiH42GMINMs7R7iP5Mbm1iImge5p/7/dPmXirKOKOBhjA3hNTiV5BlPDTQyiuuTAUEms5dY4+moswXo5zM4q9DFu6B7979o+v3kX6ZB+k3kNhP08wH82I4eJKoenN/0iCT7ALoG3ysEJf18+HEysSnniLMJr8R1pYF2QRFlqaDv3Mqyp7ipxYkt4ebMCgE7aDzT6OrfpyPowObpdjSMTUXpcwIcH8mIZCWFmyfF675zEeE0e+dHKkL1rPeCI7rr7Bqc5+1DS5YM54fk8xQwIDAQAB";
      auth-server-url = "http://localhost:9000/auth";
      ssl-required = "external";
      resource = "frontend";
      credentials = JsonObject {
        secret = "2fbf5e18-b923-4a83-9657-b4ebd5317f60";
      };
    };

    // Initialize the OAuth2 Library
    value oauth2 = oAuth2Auth.createKeycloak(vertx, password, keycloakJson);

    // first get a token (authenticate)
    oauth2.getToken(JsonObject {
      username = "user";
      password = "secret";
    }, (AccessToken|Throwable res) {
      if (is Throwable name) {
        // error handling...
      } else {
        value token = res;

        // now check for permissions
        token.isAuthorised("account:manage-account", (Boolean|Throwable r) {
          if (r) {
            // this user is authorized to manage its account
          };
        });
      };
    });