OAuth2 is a popular authentication framework. As a service provider it is thus common to provide support for OAuth2. How can you do this on a plain WebLogic Server / Service Bus without having to install additional products (and possibly have to pay for licenses)? If you just want to implement and test the code (what), see this installation manual. If you want to know more details about the implementation (how) and choices made (why), read on!
Introduction
OAuth2 client credentials flow
OAuth2 supports different flows. One of the easiest to use is the client credentials flow. It is recommended to use this flow when the party requiring access can securely store credentials. This is usually the case when there is server to server communication (or SaaS to SaaS).
The OAuth2 client credentials flow consists of an interaction pattern between 3 actors which all have their own roll in the flow.
- The client. This can be anything which supports the OAuth2 standard. For testing I’ve used Postman
- The OAuth2 authorization server. In this example I’ve created a custom JAX-RS service which generates and returns JWT tokens based on the authenticated user.
- A protected service. In this example I’ll use an Oracle Service Bus REST service. The protection consists of validating the token (authentication using standard OWSM policies) and providing role based access (authorization).
When using OAuth2, the authorization server returns a JSON message containing (among other things) a JWT (JSON Web Token).
In our case the client authenticates using basic authentication to a JAX-RS servlet. This uses the HTTP header Authorization which contains ‘Basic’ followed by Base64 encoded username:password. Of course Base64 encoded strings can be decoded easily (e.g. by using sites like these) so never use this over plain HTTP!
When this token is obtained, it can be used in the Authorization HTTP header using the Bearer keyword. A service which needs to be protected can be configured with the following standard OWSM policies for authentication: oracle/http_jwt_token_service_policy and oracle/http_jwt_token_over_ssl_service_policy and a custom policy for role based access / authorization.
JWT
JSON Web Tokens (JWT) can look something like:
This is not very helpful at first sight. When we look a little bit closer, we notice it consists of 3 parts separated by a ‘.’ character. These are the header, body and signature of the token. The first 2 parts can be Base64 decoded.
Header
The header typically consists of 2 parts (see here for an overview of fields and their meaning). The type of token and the hashing algorithm. In this case the header is
kid refers to the key id. In this case it provides a hint to the resource server on which key alias to use in its key store to validate the signature.
Body
The JWT body contains so-called claims. In this case the body is
The subject is the subject for which the token was issued. www.oracle.com is the issuer of the token. iat indicates an epoch at which the token was issued and exp indicates until when the token is valid. Tokens are valid only for a limited duration. www.oracle.com is an issuer which is accepted by default so no additional configuration was required.
Signature
The signature contains an encrypted hash of the header/body of the token. If those are altered, the signature validation will fail. To encrypt the signature, a key-pair is used. Tokens are signed using a public/private key pair.
Challenges
Implementing the OAuth2 client credentials flow using only a WebLogic server and OWSM can be challenging. Why?
- Authentication server. Bare WebLogic + Service Bus do not contain an authentication server which can provide JWT tokens.
- Resource Server. Authentication of tokens. The predefined OWSM policies which provide authentication based on JWT tokens (oracle/http_jwt_token_service_policy and oracle/http_jwt_token_over_ssl_service_policy) are picky to what tokens they accept.
- Resource Server. Authorization of tokens. OWSM provides a predefined policy to do role based access to resources: oracle/binding_permission_authorization_policy. This policy works for SOAP and REST composites and Service Bus SOAP services, but not for Service Bus REST services.
Custom components
How did I solve these challenges? I created two custom components;
- Create a simple authentication server to provide tokens which conform to what the predefined OWSM policies expect. By increasing the OWSM logging and checking for errors when sending in tokens, it becomes clear which fields are expected.
- Create a custom OWSM policy to provide role based access to Service Bus REST resources
Authentication server
The authentication server has several tasks:
- authenticate the user (client credentials)
- using the WebLogic security realm
- validate the client credentials request
- using Apache HTTP components
- obtain a public and private key for signing
- from the OPSS KeyStoreService (KSS)
- generate a token and sign it
- using the Nimbus JOSE+JWT library
Authentication
User authentication on WebLogic Server of servlets consists of 2 configuration files.
A web.xml. This file indicates
- which resources are protected
- how they are protected (authentication method, TLS or not)
- who can access the resources (security role)
The weblogic.xml indicates how the security roles map to WebLogic Server roles. In this case any user in the WebLogic security realm group tokenusers (which can be in an external authentication provider such as for example an AD or other LDAP) can access the token service to obtain tokens.
Validate the credentials request
From Postman you can do a request to the token service to obtain a token. This can also be used if the response of the token service conforms to the OAuth2 standard.
By default certificates are checked. With self-signed certificates / development environments, those checks (such as host name verification) might fail. You can disable the certificate checks in the Postman settings screen.
Also Postman has a console available which allows you to inspect requests and responses in more detail. The request looked like
Thus this is what needed to be validated; an HTTP POST request with a body containing application/x-www-form-urlencoded grant_type=client_credentials. I’ve used the Apache HTTP components org.apache.http.client.utils.URLEncodedUtils class for this.
After deployment I of course needed to test the token service. Postman worked great for this but I could also have used Curl commands like:
Accessing the OPSS keystore
Oracle WebLogic Server provides Oracle Platform Security Services.
OPSS provides secure storage of credentials and keys. A policy store can be configured to allow secure access to these resources. This policy store can be file based, LDAP based and database based. You can look at your jps-config.xml file to see which is in use in your case;
You can also look this up from the EM;
In this case the file based policy store system-jazn-data.xml is used. Presence of the file on the filesystem does not mean it is actually used! If there are multiple policy stores defined, for example a file based and an LDAP based, the last one appears to be used.
The policy store can be edited from the EM
You can create a new permission:
Codebase: file:${domain.home}/servers/${weblogic.Name}/tmp/_WL_user/oauth2/- Permission class: oracle.security.jps.service.keystore.KeyStoreAccessPermission Resource name: stripeName=owsm,keystoreName=keystore,alias=* Actions: read
The codebase indicates the location of the deployment of the authentication server (Java WAR) on WebLogic Server.
Or when file-based, you can edit the (usually system-jazn-data.xml) file directly
In this case add:
<grant> <grantee> <codesource> <url>file:${domain.home}/servers/${weblogic.Name}/tmp/_WL_user/oauth2/-</url> </codesource> </grantee> <permissions> <permission> <class>oracle.security.jps.service.keystore.KeyStoreAccessPermission</class> <name>stripeName=owsm,keystoreName=keystore,alias=*</name> <actions>*</actions> </permission> </permissions> </grant>
At the location shown below
Now if you create a stripe owsm with a policy based keystore called keystore, the authentication server is allowed to access it!
The name of the stripe and name of the keystore are the default names which are used by the predefined OWSM policies. Thus when using these, you do not need to change any additional configuration (WSM domain config, policy config). OWSM only supports policy based KSS keystores. When using JKS keystores, you need to define credentials in the credential store framework and update policy configuration to point to the credential store entries for the keystore password, key alias and key password. The provided code created for accessing the keystore / keypair is currently KSS based. Inside the keystore you can import or generate a keypair. The current Java code of the authentication server expects a keypair oauth2keypair to be present in the keystore.
Accessing the keystore and key from Java
I defined a property file with some parameters. The file contained (among some other things relevant for token generation):
keystorestripe=owsm keystorename=keystore keyalias=oauth2keypair
Accessing the keystore can be done as is shown below.
When you have the keystore, accessing keys is easy
(my key didn’t have a password but this still worked)
Generating the JWT token
After obtaining the keypair at the keyalias, the JWT token libraries required instances of RSAPrivateKey and RSAPublicKey. That could be done as is shown below
In order to sign the token, an RSAKey instance was required. I could create this from the public and private key using a RSAKey.Builder method.
Using the RSAKey, I could create a Signer
Preparations were done! Now only the header and body of the token. These were quite easy with the provided builder.
Claims:
Generate and sign the token:
Returning an OAuth2 JSON message could be done with
Role based authorization policy
The predefined OWSM policies oracle/http_jwt_token_service_policy and oracle/http_jwt_token_over_ssl_service_policy create a SecurityContext which is available from the $inbound/ctx:security/ctx:transportClient inside Service Bus. Thus you do not need a custom identity asserter for this!
However, the policy does not allow you to configure role based access and the predefined policy oracle/binding_permission_authorization_policy does not work for Service Bus REST services. Thus we need a custom policy in order to achieve this. Luckily this policy can use the previously set SecurityContext to obtain principles to validate.
Challenges
Provide the correct capabilities to the policy definition was a challenge. The policy should work for Service Bus REST services. Predefined policies provide examples, however they could not be exported from the WSM Policies screen. I did ‘Create like’ a predefined policy which provided the correct capabilities and then copied those capability definitions to my custom policy definition file. Good to know: some capabilities required the text ‘rest’ to be part of the policy name.
Also I encountered a bug in 12.2.1.2 which is fixed with the following patch: Patch 24669800: Unable to configure Custom OWSM policy for OSB REST Services. In 12.2.1.3 there were no issues.
An OWSM policy consists of two deployments
A JAR file
- This JAR contains the Java code of the policy. The Java code uses the parameters defined in the file below.
- A policy-config.xml file. This file indicates which class is implementing the policy. Important part of this file is the reference to restUserAssertion. This maps to an entry in the file below
A policy description ZIP file
- This contains a policy description file.
The description ZIP file contains a single XML file which answers questions like;
- Which parameters can be set for the policy?
- Of which type are the parameters?
- What are the default values of the parameters?
- Is it an authentication or authorization policy?
- Which bindings are supported by the policy?
The policy description file contains an element which maps to the entry in the policy-config.xml file. Also the ZIP file has a structure which is in line with the name and Id of the policy. It is like;
Thus the name of the policy is CUSTOM/rest_user_assertion_policy
This name is also part of the contents of the rest_user_assertion_policy file. You can also see there is again a reference to the implementation class and the restUserAssertion element which is in the policy-config.xml file is also there. The capabilities of the policy are mentioned in the restUserAssertion attributes.
Implementation
As indicated, for more detail see the installation manual here. The installation consists of:
- Create a stripe, keystore, keypair to use for JWT signature encrytpion and validation
- Add a system policy so the token service can access the keystore
- Create a group tokenusers which can access the token service to obtain tokens
- Deploy the token service
- Apply Patch 24669800 if you’re not on 12.2.1.3
- Copy the custom OWSM policy JAR file to the domain lib folder
- Import the policy description
If you have done the required preparations, adding OAuth2 protection to Service Bus REST services is as easy as adding 2 policies to the service and indicating which principles (can be users or groups, comma separated list) are allowed to access the service.
Finally
As mentioned before, the installation manual and code can be found here. Of course this solution does not provide all the capabilities of a product like API Platform Cloud Service, OAM, OES. Usually you don’t need all those capabilities and complexity and just a simple token service / policy providing the OAuth2 credentials flow is enough. In such cases you can consider this alternative. Mind that the entire service is protected by the policy and not specific resources. That would require extending the custom OWSM policy with this functionality. If for example someone tries to login to the token service with basic authentication and uses a wrong password for the user weblogic, it may be locked. Because of this and other resources which are available by default on the WebLogic server / Service Bus, you’ll require some extra protection when exposing this to the internet such as a firewall, IP whitelisting, SSL offloading, etc.
Great Article!! But trying to understand why to create/configure a custom OWSM policy and we could see it is validating principals but where is the JWT token validated which is passed in the HTTP authorization header.
How does adding either of the below policy works in this case and what is the action.
oracle/http_jwt_token_service_policy
oracle/http_jwt_token_over_ssl_service_policy
Thanks, This post was very helpful. Could you please describe how we can extend the custom OWSM policy for securing resources wise?
Thanks in Advance.
We are having an issue when testing the token service. The Error we get is : “Method not allowed”. Please let me know if you have inputs on this error.
Can you please provide the implementation in SOA+BPEL perspective.
Thanks for your post, it very helpful. But I have a question, I want to implement grant_type=Password Credentials, How can I do?