OAuth 2.0 and Resteasy Skeleton Key

The overall goal of Resteasy Skeleton Key is to provide a unified way for both Browser and JAX-RS clients to be secured in an integrated and seemless fashion. We want to support a network of applications and services so that if one server needs to execute or forward requests to another, there is a secure and scalable way to do this without hitting a central authentication server each and every request.

The OAuth 2.0 Authorization Framework enables a third-party to obtain access to an HTTP resource on behalf of a resource owner without the third-party having to know the credentials of the resource owner. It does this by issuing access tokens via a browser redirect protocol, or by a direct grant. The access tokens can then be transmitted by the OAuth2 Bearer Token protocol to access the protected resource.

Resteasy Skeleton Key is an OAuth 2.0 implementation that allows you to use existing JBoss AS7 security infrastructure to secure your web applications and restful services. You can turn an existing web app into an OAuth 2.0 Access Token Provider or you can turn a JBoss AS7 Security Domain into a central authentication and authorization server that a whole host of applications and services can use. Here are the features in a nutshell:

  • Turn an existing servlet-form-auth-based web application into an OAuth 2.0 provider.

  • Provide Distributed Single-Sign-On (SSO) from a central authentication server. Log in once, and you can securely access any browser-based app configured to work in the domain.

  • Provide Distributed Logout. Following one link from any application can log you out of all your distributed applications configured to use SSO.

  • Web apps can interact securely with any remote restful service by forwarding access tokens through the standard Authorization header.

  • Access tokens are digitally signed by the oauth2 framework and can be used to access any service configured to work in the domain. The tokens contain both identity and role mapping information. Because they are digitally signed, there's no need to overload the central authentication server with each request to verify identity and to determine permissions.

Important

The Resteasy distribution comes with an OAuth2 Skeleton key example. This is a great way to see OAuth2 in action and how it is configured. You may also want to use this as a template for your applications.

System Requirements

  • JBoss AS 7.1.x or higher

  • HTTPS is required. See the JBoss 7 documentation on how to enable SSL for web applications

  • Resteasy 3.0 or higher must be installed within your AS7 distribution. View how to upgrade AS7 to the latest version of Resteasy.

  • A username/password based JBoss security domain

  • Browser-based apps must be configured to use servlet FORM authentication and web.xml security constraints

Generate the Security Domain Key Pair

Access tokens are digitally signed and verified by an RSA keypair. You must generate this keypair using the JDK's keytool command or by something like openssl.

$ keytool -genkey -alias mydomain -keyalg rsa -keystore realm.jks

This will ask you a series of questions that will be used to create the X509 public certificate. Basic PKI stuff that you hopefully are already familiar with. Move this keystore file into a directory that you can reference from a configuration file. I suggest the standalone/configuration directory of your JBoss AS7 distribution.

Setting up the Auth Server

The next thing you're gonna want to do is set up a web application to be your OAuth2 provider. This can be an existing web app or you can create a new WAR to be your central authentication server. An existing web app must be configured to use servlet FORM authentication. Enabling OAuth2 within this app will not change how normal users interact with it.

Setting up your Security Domain

You can use any set of JBoss AS7 login modules you want to store your username, passwords and role mappings. Each security domain will be comprised of regular users, oauth clients, and admins. Oauth clients represent either a web application that wants to use the auth-server to do SSO, or they are traditional oauth clients that want access permision to act on behalf of another user (the traditional OAuth use case). Every oauth client must have a username, password, and a specific role mapping that gives them various permissions to participate in OAuth 2 protocols. There is a role that grants an oauth client permission to login as a specific user (default is login. This is the SSO case. There is a role that grants a client permission to request permission to act on behalf of a user (default is oauth). Additional role mappings assigned to the oauth client define what additional permissions they are allowed to have. These additional permissions are the role mappings of the application and are the intersection of the permissions given to the user the client is acting on behalf of. This is better explained by an example role mapping file:

wburke=user,admin
loginclient=login
oauthclient1=oauth,*
oauthclient2=oauth,user

In the above role mapping file with have a simple user wburke. He has application role permissions of user and admin. One oauth client user is loginclient. It has been given a role mapping of login. This client is allowed to login as the user and is given all roles of the user. The oauthclient1 user is not allowed to login as the user, but is allowed to obtain an OAuth grant to act on behalf of the user. The * role means that oauthclient1 is granted the same roles as the user it is acting on behalf of. If oauthclient1 acts on behalf of wburke then it will have both user and admin permissions. The oauthclient2 is also allowed to use the oauth grant protocol, but it will only ever be granted user permissions.

You are not confined to login, oauth, and * as role mapping names. You can configure them to be whatever you want.

Why have different login and oauth role mappings? login clients are allowed to bypass entering username and password if the user has already logged in once and has an existing authenticated session with the server. oauth clients are always required to enter username and password. You probably don't want to grant permission automatically to an oauth client. A user will want to look at who is requesting permission. This role distinction gives you this capability.

Auth Server Config File

You must create a configuration file that holds all the configuration for OAuth2. This is json formatted If you name it resteasy-oauth.json and put it within the WEB-INF/ directory of your war, that's all you have to do. Otherwise, you must specify the full path to this configuration file within a context-param within your web.xml file. The name of this param is skeleton.key.config.file. You can reference System properties within the value of this context-param by enclosing them within ${VARIABLE}. Here's an example configuration:

{
   "realm" : "mydomain",
   "admin-role" : "admin",
   "login-role" : "login",
   "oauth-client-role" : "oauth",
   "wildcard-role" : "*",
   "realm-keystore" : "${jboss.server.config.dir}/realm.jks",
   "realm-key-alias" : "mydomain",
   "realm-keystore-password" : "password",
   "realm-private-key-password" : "password",
   "access-code-lifetime" : "300",
   "token-lifetime" : "3600",
   "truststore" : "${jboss.server.config.dir}/client-truststore.ts",
   "truststore-password" : "password",
   "resources" : [
      "https://example.com/customer-portal",
      "https://somewhere.com/product-portal"
   ]
}

Let's go over what each of these config variables represent:

realm

: Name of the realm representing the users of your distributed applications and services

admin-role

: Admin role mapping used for admins. You must have this defined if you want to do distributed logout.

login-role

: Role mapping for login clients.

oauth-client-role

: Role mapping for regular oauth clients.

wildcard-role

: Role mapping for assigning all roles to an oauth client wishing to act on behalf of a user.

realm-keystore

: Absolute path pointing to the keystore that contains the realm's keypair. This keypair is used to digitally sign access tokens. You may use ${VARIABLE} to reference System properties. The example is referencing the JBoss config dir.

realm-key-alias

: Key alias for the key pair stored in your realm-keystore file.

realm-keystore-password

: Password to access the keystore.

realm-private-key-password

: Password to access the private realm key within the keystore

access-code-lifetime

: The access code is obtained via a browser redirect after you log into the central server. This access code is then transmitted in a separate request to the auth server to obtain an access token. This variable is the lifetime of this access code. In how many seconds will it expire. You want to keep this value short. The default is 300 seconds.

token-lifetime

: This is how long in seconds the access token is viable after it was first created. The default is one hour. Depending on your security requirements you may want to extend or shorten this default.

truststore

: Used for outgoing client HTTPS communications. This contains one or more trusted host certificates or certificate authorities. This is OPTIONAL if you are not using distributed logout.

truststore-password

: Password for the truststore keystore.

resources

: Root URLs of applications using this auth-server for SSO. This is OPTIONAL and only needed if you want to allow distributed logout.

Set up web.xml

Set up your security constraints however you like. You must though use FORM authentication.

Set up jboss-web.xml

In jboss-web.xml in your WEB-INF directory, point to your security domain as a normal secured web app does, and also use a specific valve.

<jboss-web>
    <security-domain>java:/jaas/commerce</security-domain>
    <valve>
        <class-name>org.jboss.resteasy.skeleton.key.as7.OAuthAuthenticationServerValve</class-name>
    </valve>
</jboss-web>

Set up jboss-deployment-structure.xml

You must import the skeleton key modules so that the classes are visible to this application. Include this file within WEB-INF

<jboss-deployment-structure>
    <deployment>
        <dependencies>
            <module name="org.jboss.resteasy.resteasy-jaxrs" services="import"/>
            <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
            <module name="org.jboss.resteasy.skeleton-key"/>
        </dependencies>
    </deployment>
</jboss-deployment-structure>

Tweak your login page

The action url used by your login form is dependent on the oauth protocol. Skeleton key defines a request attribute called OAUTH_FORM_ACTION which is the URL you should use for the form's action. Here's an example login.jsp page that uses this attribute:

<form action="<%= request.getAttribute("OAUTH_FORM_ACTION")%>" method=post>
    <p><strong>Please Enter Your User Name: </strong>
    <input type="text" name="j_username" size="25">
    <p><p><strong>Please Enter Your Password: </strong>
    <input type="password" size="15" name="j_password">
    <p><p>
    <input type="submit" value="Submit">
    <input type="reset" value="Reset">
</form>

Setting Up An App for SSO

This section specifies how you can use the central auth-server for SSO. Following these directions will use the auth-server for browser log in. The server will also be able to do bearer token authentication as well.

SSO config file

The best way to create the config file for your application is to ask the central authentication server you configured in the last section. So, boot up the auth server and go to https://*auth-server-context-root*/j\_oauth\_realm\_info.html. For example: https://localhost:8443/auth-server/j_oauth_realm_info.html. This will show template configurations depending on which valve you are using. You want the OAuthManagedResourceValve config. It will look something like this.

{
  "realm" : "mydomain",
  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCO8XXyi7oAq5ecsYy+tJrl54N2TtKAkxuWEDmzvSPU+mUA2/3qHcxucZakG74Z49410tn5IIu2CXXlk9CuKcpXvKh+cPBzmC1Nmbd+4MelRVVZnvogyPICs8h3sNTAMNdfI6hDc5/MfVQQ9m5OZrKbNR3dY50mTi/ExnJ5IWPqxQIDAQAB",
  "admin-role" : "admin",
  "auth-url" : "https://localhost:8443/auth-server/login.jsp",
  "code-url" : "https://localhost:8443/auth-server/j_oauth_resolve_access_code",
  "truststore" : "REQUIRED",
  "truststore-password" : "REQUIRED",
  "client-id" : "REQUIRED",
  "client-credentials" : {
    "password" : "REQUIRED"
  }
}

Let's go over what each of these config variables represent:

realm

: Name of the realm representing the users of your distributed applications and services

realm-public-key

: PEM format of public key.

admin-role

: Admin role mapping used for admins. You must have this defined if you want to do distributed logout.

auth-url

: URL of the auth server's login page.

code-url

: URL to turn an access code into an access token. (Part of the OAuth2 protocol)

truststore

: Used for outgoing client HTTPS communications. This contains one or more trusted host certificates or certificate authorities. This is REQUIRED as you must talk HTTPS to the auth server to turn an access code into an access token. You can create this truststore by extracting the public certificate of the auth server's SSL keystore. The google knows if you want to know how to do this.

truststore-password

: Password for the truststore keystore.

client-id

: Username of the login client. This server will send client-id and password when turning an access code into an access token. Internally, the server will do an HTTPS invocation to the auth-server passing this information using Basic AUTH.

client-credentials

: Must specify the password of the oauth login client.

Set up web.xml

Set up your security constraints however you like. You must though use FORM authentication.

Set up jboss-web.xml

In jboss-web.xml in your WEB-INF directory you need to use a specific valve.

<jboss-web>
    <valve>
        <class-name>org.jboss.resteasy.skeleton.key.as7.OAuthManagedResourceValve</class-name>
    </valve>
</jboss-web>

Set up jboss-deployment-structure.xml

You must import the skeleton key modules so that the classes are visible to this application. Include this file within WEB-INF

<jboss-deployment-structure>
    <deployment>
        <dependencies>
            <module name="org.jboss.resteasy.resteasy-jaxrs" services="import"/>
            <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
            <module name="org.jboss.resteasy.skeleton-key"/>
        </dependencies>
    </deployment>
</jboss-deployment-structure>

Bearer Token only Setup

If you have a web app that you want only to allow Bearer token authentication, i.e. a set of JAX-RS services then follow these directions.

Bearer token auth config file

The best way to create the config file for your application is to ask the central authentication server you configured in the last section. So, boot up the auth server and go to https://*auth-server-context-root*/j\_oauth\_realm\_info.html. For example: https://localhost:8443/auth-server/j_oauth_realm_info.html. This will show template configurations depending on which valve you are using. You want the BearerTokenAuthenticatorValve config. It will look something like this.

{
  "realm" : "mydomain",
  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCO8XXyi7oAq5ecsYy+tJrl54N2TtKAkxuWEDmzvSPU+mUA2/3qHcxucZakG74Z49410tn5IIu2CXXlk9CuKcpXvKh+cPBzmC1Nmbd+4MelRVVZnvogyPICs8h3sNTAMNdfI6hDc5/MfVQQ9m5OZrKbNR3dY50mTi/ExnJ5IWPqxQIDAQAB",
}

All that is needed is the realm name, and the public key of the realm. Let's go over what each of these config variables represent:

realm

: Name of the realm representing the users of your distributed applications and services

realm-public-key

: PEM format of the realm's public key. Used to verify tokens.

Set up web.xml

Set up your security constraints however you like. You must though use FORM authentication.

Set up jboss-web.xml

In jboss-web.xml in your WEB-INF directory you need to use a specific valve.

<jboss-web>
    <valve>
        <class-name>org.jboss.resteasy.skeleton.key.as7.BearerTokenAuthenticatorValve</class-name>
    </valve>
</jboss-web>

Set up jboss-deployment-structure.xml

You must import the skeleton key modules so that the classes are visible to this application. Include this file within WEB-INF

<jboss-deployment-structure>
    <deployment>
        <dependencies>
            <module name="org.jboss.resteasy.resteasy-jaxrs" services="import"/>
            <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
            <module name="org.jboss.resteasy.skeleton-key"/>
        </dependencies>
    </deployment>
</jboss-deployment-structure>

Obtaining an access token programmatically.

You can request an access token from the auth-server by doing a simple HTTPS invocation. You must use BASIC authentication to identify your user, and you will get back a signed access token for that user. Here's an example using a JAX-RS 2.0 client:

    ResteasyClient client = new ResteasyClientBuilder()
                                .truststore(truststore)
                                .build();

    Form form = new Form().param("grant_type", "client_credentials");
    ResteasyWebTarget target = client.target("https://localhost:8443/auth-server/j_oauth_token_grant");
    target.configuration().register(new BasicAuthentication("[email protected]", "password"));
    AccessTokenResponse res = target.request()
                           .post(Entity.form(form), AccessTokenResponse.class);

The above makes a simple POST to the context root of the auth server with j_oauth_token_grant at the end of the target URL. This resource is responsible for creating access tokens.

    try
    {
       Response response = client.target("https://localhost:8443/database/products").request()
                               .header(HttpHeaders.AUTHORIZATION, "Bearer " + res.getToken()).get();
       String xml = response.readEntity(String.class);
    }
    finally
    {
       client.close();
    }

The access token is a simple string. To invoke on a service protected by bearer token auth, just set the Authorization header of your HTTPS request with a value of Bearer and then the access token string.

Access remote services securely in a secure web session

If you have an application secured by one of the methods described in this chapter, you can obtain the access token of the current web session so that you can use it to invoke on other remote services securely using bearer token authentication. Each HttpServletRequest in a secure web session has an attribute called org.jboss.resteasy.skeleton.key.SkeletonKeySession which points to an instance of a class with the same name. This class contains the access token and also points to the truststore you configured. You can then extract this info and make secure remote invocations. Here's an example of that.

   public List<String> getCustomers(HttpServletRequest request)
   {
      SkeletonKeySession session = (SkeletonKeySession)request.getAttribute(SkeletonKeySession.class.getName());
      ResteasyClient client = new ResteasyClientBuilder()
                 .truststore(session.getMetadata().getTruststore())
                 .build();
      try
      {
         Response response = client.target("https://localhost:8443/database/customers").request()
                 .header(HttpHeaders.AUTHORIZATION, "Bearer " + session.getToken()).get();
         return response.readEntity(new GenericType<List<String>>(){});
      }
      finally
      {
         client.close();
      }
   }

If you are within a JAX-RS environment you can inject a SkeletonKeySession using the @Context annotation.

Check Out the OAuth2 Example!

Important

The Resteasy distribution comes with an example project that shows all of these different features in action! Check it out!

Auth Server Action URLs

For reference, here is the set of relative URL actions that the auth server will publish.

login page

: The is the url of your login page. OAuth clients will redirect to it. This is application specific.

j_oauth_resolve_access_code

: Used by oauth clients to turn an access code into an access token.

j_oauth_logout

: Do a GET request to this URL and it will perform a distributed logout.

j_oauth_token_grant

: Do a POST with BASIC Auth to obtain an access token for a specific user.

j_oauth_realm_info.html

: Displays an HTML page with template configurations for using this realm.

results matching ""

    No results matching ""