Implementing OAuth 2 with Oracle Access Manager OAuth Services (Part V)

Introduction

This post is part of a series of posts about OAM’s OAuth implementation.

Other posts can be found here:

Part I – explains the proposed architecture and how to enable and configure OAM OAuth Services.

Part II – describes a Business to Business use-case (2-legged flow);

Part III  – deals with the Customer to Business use-case (3-legged flow), when the client code is running in the application server;

Part IV – describes the Customer to Business use-case (3-legged flow), when the client application runs on the browser, embedded as Javascript code;

Part V  – provides the source code and additional information for the use case implementation.

The previous posts explained how to configure OAM OAuth Server and how the different use-cases work.

This last post will discuss the Access Tokens and Refresh Tokens usage and Tokens Validation strategy on the Resource Server side.

Both topics are directly related to the client application design, security and overall system performance.

And last, but not least, source code will be provided so that it can be used as a starting point to understand the OAM’s OAuth basic implementation.

Oracle recommends that customers install the latest bundle patch available for their specific IdM release. Bundle Patches might include important patches for OAuth implementation. You can find information about Bundle Patch History and Releases in the Support document “OAM Bundle Patch Release History (Doc ID 736372.1)”. Please visit My Oracle Support (https://support.oracle.com) and search for Doc ID 736372.1.

Before You Proceed…

Read this if you’re using OAM R2PS3 (11.1.2.3)

This post was written based on OAM R2PS2 release. Even though most things are still the same on R2PS3 release, there are some subtle differences from one release to the other.
In OAM R2PS3, you need to deploy a webgate in front of your OAM servers in order to use the OAuth 3-legged flow.
The webgate is required to protect the OAuth consent page, otherwise you will get an error when trying to follow the 3-legged OAuth flow.
Review the documentation on how to install a supported webserver and deploy webgate here.

Deploy and register a webserver/webgate and configure the following resources in your application domain:

/ms_oauth/oauth2/oammsui/** – Excluded
/oam/** – Excluded
/ms_oauth/img/* – Excluded
/ms_oauth/style/* – Excluded
/ms_oauth/oauth2/endpoints/** – Excluded
/ms_oauth/oauth2/ui/** – Protected

In your webserver create a new conf file with the following directives

<Location /oam>
SetHandler weblogic-handler
WLProxySSL ON
WLProxySSLPassThrough ON
WLCookieName OAM_JSESSIONID
WebLogicCluster server1:port1, server2:port2
</Location>

<Location /ms_oauth>
SetHandler weblogic-handler
WLProxySSL ON
WLProxySSLPassThrough ON
WLCookieName OAM_JSESSIONID
WebLogicCluster server1:port1, server2:port2
</Location>

Access Token and Refresh Token Usage

In the examples provided in this post series, there is no Access Token reutilization or Refresh Token usage.

In a real application scenario, once the client application obtains the Access Token, it would probably make several calls using the same token, as long as it remains valid.

If the Access Token has expired –  and the client application should be able to check its validity before making a call – the client application can request another Access Token using the Refresh Token and its client credentials.

This way, the Client Application avoids making an additional call to the OAuth server every time it needs to call a service in the Resource Server.

In real world scenarios, it would really hurt the system performance, as under real load it is not practical to request a new token each time a call to the Resource Service is made.

There is also a security reason behind Refresh Tokens.

Because Access Tokens are short-lived (in contrast to long-lived Refresh Tokens), if they are compromised or the end user wishes to revoke the client application access, it can be done by revoking the Client Application Tokens (Access and Refresh tokens) and denying its access to all user resources.

This way, even if the application still has a valid Access Token or Refresh Token cached it cannot be used anymore to request access to resources or to request additional Access Tokens.

Remember, Refresh Tokens are obtained exchanging the client credentials with the Authorization Server, thus, by revoking the Client Application access (and all its tokens) the user can deny access to his resources at any time, without compromising security and other Client Applications access.

Token Validation

The token validation in the examples provided on this post rely on an additional call to the OAuth server.

This can degrade the OAuth server and Resource Service performance as these calls adds an additional chunk of time and processing to system when under load.

A good approach is to validate the token locally, using the OAuth server signing key to validate the signature and the Access Token payload.

By default, OAM uses the public certificate under the alias “oracert” to sign the OAuth tokens.

This certificate can be found in <DOMAIN_HOME>/config/fmwconfig/default-keystore.jks keystore.

To export the certificate run the following keytool command:

keytool -exportcert -alias “oracert” -keystore /u02/oracle/domains/OAMDomain/config/fmwconfig/default-keystore.jks -file oracert.der -storepass <STORE_PASS>

In a vanilla installation, the default-keystore password should be the same as OAM keystore password, which ca be found using the following WLST command:

listCred(map=”OAM_STORE”, key=”jks”)

There are lots of OAuth toolkits out there that can be used to validate tokens locally.

A simple code implementation using one of the available toolkits would look like this:

public boolean validateJWT(String token, String scope) {
    boolean isValid = false;
    try {
        JWSObject jwsObject = JWSObject.parse(token);
        JWSVerifier verifier = new RSASSAVerifier(getPublicKey("oracert.der"));
        boolean isValid = jwsObject.verify(verifier);

        if(isValid) {
            String tk_payload = jwsObject.getPayload().toString();
            JSONObject obj = new JSONObject(tk_payload);

            String userID = obj.getString("prn");

            String tokenScope = obj.getString("oracle.oauth.scope");

            if(tokenScope != null && !tokenScope.contains(scope)){
                isValid = false;
            }
        }
    } catch (Exception e) {
        isValid = false;
        e.printStackTrace();
    }
    return isValid;
}
private RSAPublicKey getPublicKey(String filename) throws Exception {
    InputStream inStream = null;
    try {
        inStream = getClass().getClassLoader().getResourceAsStream("certs/"+filename);
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate cert = (X509Certificate)cf.generateCertificate(inStream);
        return (RSAPublicKey)cert.getPublicKey();
    } finally {
        if (inStream != null) {
            inStream.close();
        }
    }
}

Source Code

The source code is provided “AS IS” with no express or implied warranty for accuracy or accessibility.

The code is indented to demonstrate the basic OAuth/OAM features and does not represent, by any means, the recommended approach or is intended to be used in development or productions environments.

That being said, download the source_code.

The examples are divided into two applications “ClientWebApp” and “ResourceService”.

ClientWebApp contains the code for the client part, which will obtain a OAuth Token from OAM and make calls to the REST endpoints in the ResourceService application.

ResourceService will expose REST endpoints, which will receive calls from the ClientWebApp.

The ResourceService application will validate the OAuth Token and make a decision if to reply back with the requested data or not.

The source code is self explanatory and commented so one can follow up with the implementation.

Once the OAM configuration for the OAuth artifacts is done, as explained in Part I of this post series, one can adjust the URLs in the source code, compile, and deploy both applications to any servlet container, and test the complete scenario.

I hope the posts series could be of help to understand the basics of OAM’s OAuth implementation, enjoy!

Comments

  1. Androklis Gregoriou says:

    Hello,

    Excellent article.
    Is there any way to apply branding on user consent page when user is asked to provide authorization?

    Thank you in advance.

  2. Pratik Sharma says:

    Hi,

    Excellent article.
    But Even I am facing the same issue as ramesht in PS3 in C2B case. The http://xxx.xxx.xx.xx:14100/ms_oauth/oauth2/endpoints/oauthservice/authorize
    is getting re-directed to
    http://xxx.xx.xxx.xx:14100/ms_oauth/oauth2/ui/oauthservice/showconsent?response_type=code&client_id=browserClient&redirect_uri=…………..
    without asking for OAM authentication. It goes in a loop as authorization code is always null in request.

    Thanks

    • Pratik, I recommend that you open a support ticket to review your installation and provide you guidance.
      I have heard people getting this issue before with PS3.
      This post was based on PS2, so there might be some differences between the 2 versions.
      Thanks,
      Paulo.

  3. Saikumar TV says:

    Hi Paulo,

    I have use-case where I need to configure OAM PS3 to allow third-party JWT bearer token and provide access token. I have followed this guide 53.4.3 Configuring a Third-Party JWT Trust Issuer (https://docs.oracle.com/cd/E52734_01/oam/AIAAG/oicconfigoauth.htm#AIAAG89803). But no luck . I am getting an error whenever I make call to Request an User Token using client assertion, it’s throwing in HTTP/1.1 401 Unauthorized error and {“error”:”invalid_client”,”error_description”:”Invalid client credentials”}.

    I imported the SSL certificate of the Third party issuer to OAM dfault-keystore.jks but still no luck.

    curl -i -H ‘Content-Type: application/x-www-form-urlencoded; charset=utf-8’ –request POST ‘http://oam.oracledemo.com:14100/ms_oauth/oauth2/endpoints/oauthservice/tokens’ -d ‘grant_type=password&username=user1&password=password&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=’

    Thanks

    Sai

  4. Hi RameshT and Paulo,

    I am in a same situation, where I am using PS2 and when requesting Authorization Code via browser, I get redirected to showconsent page with 401 unauthorized error.

    Could you please advise what you did to get away from showconsent page.

    Thanks
    Ghanny

  5. I’m not able to get the 3-legged flow working and was curious if this is supported if OAuth/SSO services are behind a DCC webgate.

  6. Hi Paulo,

    Thank you for the excellent article and well thought sample program.

    I have successfully compiled and deployed the application. However, I have encountered a few problems which I can not find answers. Hope you can help me out. It seems I am almost there.

    ClientWebApp and Resource Service App are running in tomcat server different than the server where OAM is hosted.

    I can get the authorization token through the initial code of index.html of ClientWebApp, however it cannot obtain access_token from the authorization code. Please note that in order to simplify I have removed the optional state portion, including which get the same error. Here is the equivalent curl and corresponding error. In order to exclude, any privilege related issues I have progressively given all the privileges “browserClient” client.

    curl -i -H “Authorization: Basic YnJvd3NlckNsaWVudDpnOGFHZ2xaRHJQRGw3ZQ==” -H “Content-Type: application/x-www-form-urlencoded;charset=UTF-8” –request POST http://XXX.xx.xx.xxx:14100/ms_oauth/oauth2/endpoints/oauthservice/tokens -d “redirect_uri=http://XX.XXX.XX.xxx:8080/ClientWebApp/index.html&grant_type=authorization_code&code=eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6Im9yYWtleSJ9.ey..Xs”

    I get the following error:

    HTTP/1.1 401 Unauthorized
    ……
    ……
    {“error”:”invalid_grant”,”error_description”:”Invalid Grant: grant_type=authorization_code”}

    I tried different different grant type (password and username) and successfully obtained the access code, however I cannot validate the access code I have obtained.

    I get the following error
    HTTP/1.1 401 Unauthorized
    ……
    ……
    {“error”:”invalid_grant”,”error_description”:”oracle.security.jps.internal.trust.token.TokenProviderException: Validate operation failed.”}

    Appreciate any assistance.
    Thanks a lot.

    • Hi Ramesh, make sure your client, browserClient, is configured properly in OAM. Verify if is has the “Authorization Code” grant type enabled for it and that the HTTP Redirect URIs you configured for you client matches the one you’re passing in your curl command. From the Part I of this post series, it should be “https://oxygen.mycompany.com:7502/ClientWebApp/index.html”, if you modify the redirect URI for your specific needs, you have to update the client definitions as well. Your curl command shows: redirect_uri=http://XX.XXX.XX.xxx:8080/ClientWebApp/index.html.

      If the above does not help, you can find more detailed information in the OAM Diagnostics logs.

      Enable trace level for the Token Engine: https://docs.oracle.com/cd/E40329_01/admin.1112/e27239/logging.htm#AIAAG1156.

  7. Paulo,

    While deploying the source code on weblogic, I get following exception;

    l>> <Failure occurred in the execution of deployment request with ID '144554061216
    9' for task '13'. Error is: 'weblogic.application.ModuleException: Failed to load webapp: 'ResourceService.war''
    weblogic.application.ModuleException: Failed to load webapp: 'ResourceService.war'
    at weblogic.servlet.internal.WebAppModule.prepare(WebAppModule.java:395)
    …….
    Caused By: java.lang.ClassNotFoundException: com.oracle.ateam.examples.filter.OAuthTokenValidationFilter
    at weblogic.utils.classloaders.GenericClassLoader.findLocalClass(GenericClassLoader.java:297)
    at weblogic.utils.classloaders.GenericClassLoader.findClass(GenericClassLoader.java:270)

    I see, that it has OAuthTokenValidationFilter.java but may be we are missing the class file or jar file; do you have the class/jar file for this.

    0 Thu Oct 22 06:56:58 PDT 2015 WEB-INF/classes/com/oracle/ateam/examples/filter/
    688 Wed Sep 30 11:03:08 PDT 2015 WEB-INF/classes/com/oracle/ateam/examples/filter/CustResponseWrapper.java
    3150 Wed Sep 30 11:05:06 PDT 2015 WEB-INF/classes/com/oracle/ateam/examples/filter/OAuthTokenValidationFilter.java
    0 Thu Oct 22 06:56:58 PDT 2015 WEB-INF/classes/com/oracle/ateam/examples/oauth/
    0 Thu Oct 22 06:56:58 PDT 2015 WEB-INF/classes/com/oracle/ateam/examples/oauth/validation/
    3899 Wed Sep 30 11:07:08 P

    Thanks
    Ghanny

    • Ghanny, the source code are provided as Eclipse projects. Import them into Eclipse and resolve all depencies. Compile the project and export it as a war file. All the required jars (except for javax.servlet_1.0.0.0_2-5.jar) are included in the “lib” folder. Copy this folder to the WEB-INF folder of your application and try to compile and export your project again. The servlet jar you can find in your weblogic BEA_HOME/modules/ folder. Include it in your application build path.

  8. Hi Paulo,

    Great article and series; very useful!!

    I am very excited to try these use cases which you have put together, but I am not from developer background and not very clear how I can deploy the ResourceService and ClientApp using the source code. Will it be possible for you to please provide some high level steps on deploying the source code.

    Thanks

    • Hi Paulo,

      Someone in the different forum suggested that it does not work in 11gR2PS2, but it works in 11gR2PS3. I upgraded. Following which, I could run your program, however instead of authorization, I needed to change it to grant_type=password&username. With this type I could obtain access_token and validate properly.

      However, what used to work in 11gr2PS2, obtaining the authorization code from OAM ceased to work. Please note that browserClient has “Authorization Code” priviledge. In curl command I get the following response.

      curl -i –request GET “http://xxx.xxx.xx.xx:14100/ms_oauth/oauth2/endpoints/oauthservice/authorize?response_type=code&client_id=browserClient&redirect_uri=http://xx.xx.xxx.xxx:8080/ClientWebApp/index.html
      &scope=UserProfile.me”

      I get the following message and do not get the authorization code:

      HTTP/1.1 302 Moved Temporarily
      ….
      Location: http://xxx.xxx.xx.xx:14100/ms_oauth/oauth2/ui/oauthservice/showconsent?response_type=code&client_id=browserClient…….

      Is there any obvious mistake ?
      Thanks

      • Hi Ramesht,

        when you make a curl request to http://xxx.xxx.xx.xx:14100/ms_oauth/oauth2/endpoints/oauthservice/authorize you are asking for an authorization code. Note that you get a 302 as response, that would redirect your client to the consent page. If the user gives his consent, after identifying himself by authenticating to the server, the authorization code will be appended to the redirect_uri URL parameter. Your application code should be able to retrieve the authorization code from the URL parameter and request an Access Token, using its client credentials and the authorization code. This flow is intedend to be used with a browser, that can follow redirects and render the user login and consent page.

        • Hi Paulo,

          Thnanks for your reponse.
          I have disabled consent requirement in both the client and resource. In the past with this setting, I would get the code directly in response.
          Does n’t work either in the browser, if I copy and paste the Location url (
          http://xxx.xx.xxx.xx:14100/ms_oauth/oauth2/ui/oauthservice/showconsent?response_type=code&client_id=browserClient&redirect_uri=…………..
          , i get
          http://xxxxxxxxx:8080/ClientWebApp/index.html?error=server_error, and code=null (debug console output)from the inside the code.

          Thanks

          • Hi Paulo,

            It seems like I am almost there. If something is immediately obvious, please let me me know. Here is the situation.

            With version PS2, using your sample code, I was redirected to oam login page and the authorization code was returned to me, however I could not obtain access_token. I modified your code, then Or, I could obtain the access_token using “Resource Owner Credentials”, however validate operation for the same token was failing.

            I upgraded the same PS2 to PS3, and now I can obtain the access_code using “Resource Owner Credentials”, and validate the access_token and retrieve attributes, using your sample code. However, I am presented with different problem. As stated in my previous post, I cannot seem to get authorization code and I am presented with 302 redirection. What I have found is, if I have an active session in the browser, I can get the authorization code. So my conclusion is, OAM is not redirecting me to the authentication page, if I am not authenticated. In PS2 version before the upgrade for the same resource I was redirected to the OAM login page. Could you indicate what may be wrong here, is there some setting somewhere to tell the OAM to redirect to authentication page. Here the protected resource is the default resource “UserProfile.me”.

            Thanks a lot !

Add Your Comment