== The OAuth2 auth provider

This component contains an out of the box OAuth2 Relay Party implementation.

To use this project, add the following
dependency to the _dependencies_ section of your build descriptor:

* Maven (in your `pom.xml`):

[source,xml,subs="+attributes"]
----
<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-auth-oauth2-scala_2.12</artifactId>
  <version>3.5.2.CR2</version>
</dependency>
----

* Gradle (in your `build.gradle` file):

[source,groovy,subs="+attributes"]
----
compile 'io.vertx:vertx-auth-oauth2-scala_2.12:3.5.2.CR2'
----

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 http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.1[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 http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.3[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 http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.4[Oauth2 specification, section 4.4].

=== Extensions

The provider supports RFC7523 an extension to allow server to server authorization based on JWT.

=== Getting Started

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

[source,scala]
----

var oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.AUTH_CODE, OAuth2ClientOptions()
  .setClientID("YOUR_CLIENT_ID")
  .setClientSecret("YOUR_CLIENT_SECRET")
  .setSite("https://github.com/login")
  .setTokenPath("/oauth/access_token")
  .setAuthorizationPath("/oauth/authorize")
)

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

var authorization_uri = oauth2.authorizeURL(new io.vertx.core.json.JsonObject().put("redirect_uri", "http://localhost:8080/callback").put("scope", "notifications").put("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:


var code = "xxxxxxxxxxxxxxxxxxxxxxxx"

oauth2.authenticateFuture(new io.vertx.core.json.JsonObject().put("code", code).put("redirect_uri", "http://localhost:8080/callback")).onComplete{
  case Success(result) => {
    // error, the code provided is not valid
  }
  case Failure(cause) => {
    println(s"$cause")
  }
}

----

==== 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.

[source,scala]
----

// Set the client credentials and the OAuth2 server
var credentials = OAuth2ClientOptions()
  .setClientID("<client-id>")
  .setClientSecret("<client-secret>")
  .setSite("https://api.oauth.com")



// Initialize the OAuth2 Library
var oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.AUTH_CODE, credentials)

// Authorization oauth2 URI
var authorization_uri = oauth2.authorizeURL(new io.vertx.core.json.JsonObject().put("redirect_uri", "http://localhost:8080/callback").put("scope", "<scope>").put("state", "<state>"))

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

var tokenConfig = new io.vertx.core.json.JsonObject().put("code", "<code>").put("redirect_uri", "http://localhost:3000/callback")

// Callbacks
// Save the access token
oauth2.authenticateFuture(tokenConfig).onComplete{
  case Success(result) => {
    System.err.println(s"Access Token Error: ${res.cause().getMessage()}")
  }
  case Failure(cause) => {
    println(s"$cause")
  }
}

----

==== 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.

[source,scala]
----

// Initialize the OAuth2 Library
var oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.PASSWORD)

var tokenConfig = new io.vertx.core.json.JsonObject().put("username", "username").put("password", "password")

// Callbacks
// Save the access token
oauth2.authenticateFuture(tokenConfig).onComplete{
  case Success(result) => {
    System.err.println(s"Access Token Error: ${res.cause().getMessage()}")
  }
  case Failure(cause) => {
    println(s"$cause")
  }
}

----

==== Client Credentials Flow

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

[source,scala]
----

// Set the client credentials and the OAuth2 server
var credentials = OAuth2ClientOptions()
  .setClientID("<client-id>")
  .setClientSecret("<client-secret>")
  .setSite("https://api.oauth.com")



// Initialize the OAuth2 Library
var oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.CLIENT, credentials)

var tokenConfig = new io.vertx.core.json.JsonObject()

// Callbacks
// Save the access token
oauth2.authenticateFuture(tokenConfig).onComplete{
  case Success(result) => {
    System.err.println(s"Access Token Error: ${res.cause().getMessage()}")
  }
  case Failure(cause) => {
    println(s"$cause")
  }
}

----

=== 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.

[source,scala]
----
// Check if the token is expired. If expired it is refreshed.
if (token.expired()) {
  // Callbacks
  token.refreshFuture().onComplete{
    case Success(result) => {
      // success
    }
    case Failure(cause) => {
      println(s"$cause")
    }
  }
}

----

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

[source,scala]
----
// Revoke only the access token
token.revokeFuture("access_token").onComplete{
  case Success(result) => println("Success")
  case Failure(cause) => println("Failure")
}

----

=== Example configuration for common OAuth2 providers

For convenience there are several helpers to assist your with your configuration. Currently we provide:

* Azure Active Directory `link:../../scaladocs/io/vertx/scala/ext/auth/oauth2/providers/AzureADAuth.html[AzureADAuth]`
* Box.com `link:../../scaladocs/io/vertx/scala/ext/auth/oauth2/providers/BoxAuth.html[BoxAuth]`
* Dropbox `link:../../scaladocs/io/vertx/scala/ext/auth/oauth2/providers/DropboxAuth.html[DropboxAuth]`
* Facebook `link:../../scaladocs/io/vertx/scala/ext/auth/oauth2/providers/FacebookAuth.html[FacebookAuth]`
* Foursquare `link:../../scaladocs/io/vertx/scala/ext/auth/oauth2/providers/FoursquareAuth.html[FoursquareAuth]`
* Github `link:../../scaladocs/io/vertx/scala/ext/auth/oauth2/providers/GithubAuth.html[GithubAuth]`
* Google `link:../../scaladocs/io/vertx/scala/ext/auth/oauth2/providers/GoogleAuth.html[GoogleAuth]`
* Instagram `link:../../scaladocs/io/vertx/scala/ext/auth/oauth2/providers/InstagramAuth.html[InstagramAuth]`
* Keycloak `link:../../scaladocs/io/vertx/scala/ext/auth/oauth2/providers/KeycloakAuth.html[KeycloakAuth]`
* LinkedIn `link:../../scaladocs/io/vertx/scala/ext/auth/oauth2/providers/LinkedInAuth.html[LinkedInAuth]`
* Mailchimp `link:../../scaladocs/io/vertx/scala/ext/auth/oauth2/providers/MailchimpAuth.html[MailchimpAuth]`
* Salesforce `link:../../scaladocs/io/vertx/scala/ext/auth/oauth2/providers/SalesforceAuth.html[SalesforceAuth]`
* Shopify `link:../../scaladocs/io/vertx/scala/ext/auth/oauth2/providers/ShopifyAuth.html[ShopifyAuth]`
* Soundcloud `link:../../scaladocs/io/vertx/scala/ext/auth/oauth2/providers/SoundcloudAuth.html[SoundcloudAuth]`
* Stripe `link:../../scaladocs/io/vertx/scala/ext/auth/oauth2/providers/StripeAuth.html[StripeAuth]`
* Twitter `link:../../scaladocs/io/vertx/scala/ext/auth/oauth2/providers/TwitterAuth.html[TwitterAuth]`

==== JBoss Keycloak

When using this Keycloak 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:

[source,scala]
----
// you would get this config from the keycloak admin console
var keycloakJson = new io.vertx.core.json.JsonObject().put("realm", "master").put("realm-public-key", "MIIBIjANBgkqhk...wIDAQAB").put("auth-server-url", "http://localhost:9000/auth").put("ssl-required", "external").put("resource", "frontend").put("credentials", new io.vertx.core.json.JsonObject().put("secret", "2fbf5e18-b923-4a83-9657-b4ebd5317f60"))

// Initialize the OAuth2 Library
var oauth2 = KeycloakAuth.create(vertx, OAuth2FlowType.PASSWORD, keycloakJson)

// first get a token (authenticate)
oauth2.authenticateFuture(new io.vertx.core.json.JsonObject().put("username", "user").put("password", "secret")).onComplete{
  case Success(result) => {
    // error handling...
  }
  case Failure(cause) => {
    println(s"$cause")
  }
}

----

We also provide a helper class for Keycloak so that we can we can easily retrieve decoded token and some necessary
data (e.g. `preferred_username`) from the Keycloak principal. For example:

[source,scala]
----
// you can get the decoded `id_token` from the Keycloak principal
var idToken = KeycloakHelper.idToken(principal)

// you can also retrieve some properties directly from the Keycloak principal
// e.g. `preferred_username`
var username = KeycloakHelper.preferredUsername(principal)

----

==== Google Server to Server

The provider also supports Server to Server or the RFC7523 extension. This is a feature present on Google with their
service account.

=== Token Introspection

Tokens can be introspected in order to assert that they are still valid. Although there is RFC7660 for this purpose
not many providers implement it. Instead there are variations also known as `TokenInfo` end points. The OAuth2
provider will accept both end points as a configuration. Currently we are known to work with `Google` and `Keycloak`.

Token introspection assumes that tokens are opaque, so they need to be validated on the provider server. Every time a
token is validated it requires a round trip to the provider. Introspection can be performed at the OAuth2 level or at
the User level:

[source,scala]
----
// OAuth2Auth level
oauth2.introspectTokenFuture("opaque string").onComplete{
  case Success(result) => {
    // token is valid!
    var accessToken = result
  }
  case Failure(cause) => println("Failure")
}

// User level
token.introspectFuture().onComplete{
  case Success(result) => {
    // Token is valid!
  }
  case Failure(cause) => println("Failure")
}

----

=== Verifying JWT tokens

We've just covered how to introspect a token however when dealing with JWT tokens one can reduce the amount of trips
to the provider server thus enhancing your overall response times. In this case tokens will be verified using the
JWT protocol at your application side only. Verifying JWT tokens is cheaper and offers better performance, however
due to the stateless nature of JWTs it is not possible to know if a user is logged out and a token is invalid. For
this specific case one needs to use the token introspection if the provider supports it.

[source,scala]
----
// OAuth2Auth level
oauth2.decodeTokenFuture("jwt-token").onComplete{
  case Success(result) => {
    // token is valid!
    var accessToken = result
  }
  case Failure(cause) => println("Failure")
}

----

Until now we covered mostly authentication, although the implementation is relay party (that means that the real
authentication happens somewhere else), there is more you can do with the handler. For example you can also do
authorization if the provider is known to support JSON web tokens. This is a common feature if your provider is a
OpenId Connect provider or if the provider does support `access_token`s as JWTs.

Such provider is Keycloak that is a OpenId Connect implementation. In that case you will be able to perform
authorization in a very easy way.

== Authorization with JWT tokens

Given that Keycloak does provide `JWT` `access_token`s one can authorize at two distinct levels:

* role
* authority

To distinct the two, the auth provider follows the same recommendations from the base user class, i.e.: use the`:` as
a separator for the two. It should be noted that both role and authorities do not need to be together, in the most
simple case an authority is enough.

In order to map to keycloak's token format the following checks are performed:

1. If no role is provided, it is assumed to the the provider realm name
2. If the role is `realm` then the lookup happens in `realm_access` list
3. If a role is provided then the lookup happends in the `resource_access` list under the role name

=== Check for a specific authorities

Here is one example how you can perform authorization after the user has been loaded from the oauth2 handshake, for
example you want to see if the user can `print` in the current application:

[source,scala]
----
user.isAuthorisedFuture("print").onComplete{
  case Success(result) => println("Success")
  case Failure(cause) => println("Failure")
}

----

However this is quite specific, you might want to verify if the user can `add-user` to the whole system (the realm):

[source,scala]
----
user.isAuthorisedFuture("realm:add-user").onComplete{
  case Success(result) => println("Success")
  case Failure(cause) => println("Failure")
}

----

Or if the user can access the `year-report` in the `finance` department:

[source,scala]
----
user.isAuthorisedFuture("finance:year-report").onComplete{
  case Success(result) => println("Success")
  case Failure(cause) => println("Failure")
}

----

== Token Management

=== Check if it is expired

Tokens are usually fetched from the server and cached, in this case when used later they might have already expired
and be invalid, you can verify if the token is still valid like this:

[source,scala]
----
// internal validation against, expiration date
var isExpired = user.expired()

----

This call is totally offline, it could still happen that the Oauth2 server invalidated your token but you get a non
expired token result. The reason behind this is that the expiration is checked against the token expiration dates,
not before date and such values.

=== Refresh token

There are times you know the token is about to expire and would like to avoid to redirect the user again to the login
screen. In this case you can refresh the token. To refresh a token you need to have already a user and call:

[source,scala]
----
user.refreshFuture().onComplete{
  case Success(result) => {
    // the refresh call succeeded
  }
  case Failure(cause) => {
    println(s"$cause")
  }
}

----

=== Revoke token

Since tokens can be shared across various applications you might want to disallow the usage of the current token by
any application. In order to do this one needs to revoke the token against the Oauth2 server:

[source,scala]
----
user.revokeFuture("access_token").onComplete{
  case Success(result) => {
    // the refresh call succeeded
  }
  case Failure(cause) => {
    println(s"$cause")
  }
}

----

It is important to note that this call requires a token type. The reason is because some providers will return more
than one token e.g.:

* id_token
* refresh_token
* access_token

So one needs to know what token to invalidate. It should be obvious that if you invalidate the `refresh_token` you're
still logged in but you won't be able to refresh anymore, which means that once the token expires you need to redirect
the user again to the login page.

=== Introspect

Introspect a token is similar to a expiration check, however one needs to note that this check is fully online. This
means that the check happens on the OAuth2 server.

[source,scala]
----
user.introspectFuture().onComplete{
  case Success(result) => {
    // the introspection call succeeded
  }
  case Failure(cause) => {
    println(s"$cause")
  }
}

----

Important note is that even if the `expired()` call is `true` the return from the `introspect` call can still be an
error. This is because the OAuth2 might have received a request to invalidate the token or a loggout in between.

=== Logging out

Logging out is not a `Oauth2` feature but it is present on `OpenID Connect` and most providers do support some sort
of logging out. This provider also covers this area if the configuration is enough to let it make the call. For the
user this is as simple as:

[source,scala]
----
user.logoutFuture().onComplete{
  case Success(result) => {
    // the logout call succeeded
  }
  case Failure(cause) => {
    println(s"$cause")
  }
}

----