Posted Jul 30 by Pete Oliver.
Updated Aug 11.

This article details how to configure OTDS 10.5 Patch 6 as an OAuth 2.0 Authorization Server and walks through an example scenario where access to a RESTful API is authorized with the OAuth 2.0 Resource Owner Password Credentials Grant.

Last activity Oct 17 by Tushar Sonwalkar.
7275 views. 2 comments.

OAuth 2.0 Authentication with OTDS 10.5 Patch 6

This article details how to configure OTDS 10.5 Patch 6 as an OAuth 2.0 Authorization Server and walks through an example scenario where access to a RESTful API is authorized with the OAuth 2.0 Resource Owner Password Credentials Grant.

Prerequisites

  • OTDS 10.5 Patch 6 (or later). You can download it from the OpenText Knowledge Centre here.

Install and Configure OTDS

Refer to the OTDS documentation for details on installation and basic configuration.

This article will assume that your OTDS host address is http://localhost:8080.

You will need at least a non-synchronized user partition with user entries configured for login with password credentials. In other words, if you have a user that can sign in to your OTDS sign in page: http://localhost:8080/otdsws/login then you're good to go (at minimum, this can be your adminstrative user otadmin@otds.admin).

Enable OAuth 2.0 over HTTP

OAuth 2.0 requires that all OAuth 2.0 traffic happen over an SSL / TLS secured channel. By default, OTDS observes this rule, but for development reasons may be configured to permit OAuth 2.0 traffic over HTTP. It is suggested you enable this in your development environment to aid in debugging.

To access the OTDS administration UI, point your browser at http://localhost:8080/otds-admin/.

Sign in to the OTDS web admin console as user otadmin@otds.admin, and locate the System Attributes page. Click on Add and add a system attribute with the name directory.auth.EnforceSSL and value false, then click Save:

Enable OTDS OAuth 2.0 over HTTP

Create your OTDS OAuth 2.0 Client Partition

For now, registration of your client is a manual process. OTDS represents each OAuth 2.0 client as a user in a special User Partition named OAuthClients. Create this partition:

Create Non-Synchronized OTDS User Partition

Name the new User Partition

You will need to view the Partition Members in order to create a user that will represent your OAuth 2.0 client:

View OTDS Members

OTDS New User Menu Item

Note: This article assumes the client being created is not able to keep OAuth 2.0 client secrets confidential (e.g. A browser-based web application, or a native mobile client), so we do not specify a password for the 'user' here. Give the client a name and click Save:

Save the new user

Note: OTDS assumes all clients are created in the root Location (Organizational Unit) of your OAuthClients User Partition. Verify this on the Users tab for your partition:

Verify your user is in the root location

Create an OTDS Resource that represents your Service / OAuth 2.0 Resource

Your client will access your resource via an API. OTDS needs to be aware of your resource, and this is modelled with the OTDS Resource object. You will need to create an OTDS resource and assign your users to the Access Role created for that resource. Complete the following steps.

Create a new OTDS Resource from the Resource tab:

OTDS Resources Tab

Simply give it a name and hit Save:

Save the OTDS Resource

You will be presented with a screen telling you that you need to activate the new resource. This is a once-only operation, and usually happens from within your OpenText server product when you enable OTDS integration.

OTDS activate resource popup

Copy the resource identifier and make a note of the resource name.

For the purpose of this article, we'll use the OTDS Swagger page to activate the resource. In your browser, visit http://localhost:8080/otdsws/api/#!resources and expand the Activate a resource section. Enter the resource identifier from the previous step, and hit Try it out!. All being well, you'll see a response code of 200. You will also see a response value secret_key, which you may ignore for the purposes of this tutorial:

OTDS Using Swagger to Activate Resource

Next, you will need to give some users access to the newly created resource. In this example, I'm going to grant any user in the administrative User Partition access to my Resource.

In the admin UI, locate the OTDS Access Role that was automatically created for your Resource, then click Actions / View Access Role Details:

OTDS Access Role Details

To make sure the users you wish to authenticate against the new resource, ensure they or a container they belong to is added to access role. Here, I add the entire otds.admin partition since I'll be testing with the otadmin@otds.admin user:

OTDS add to access role

OTDS add to access role

Close the dialog box then click on Save:

OTDS add to access role

You're now ready to use OAuth 2.0 for authentication.

Obtaining an OAuth 2.0 Access Token and Refresh Token

Both the OAuth 2.0 token and authentication end points exposed by OTDS are to be found at http://localhost:8080/otdsws/login. To request an Access Token, you need to POST the following parameters to this endpoint:

+------------+----------------------------+------------------------------------------------------+
| Name       | Value                      | Notes                                                |
+------------+----------------------------+------------------------------------------------------+
| grant_type | "password"                 | Informs the OAuth server that this request           |
|            |                            | is for Resource Owner Password Credentials           |
|            |                            | Grant                                                |
+------------+----------------------------+------------------------------------------------------+
| client_id  | <client-id>                | The name of the client - in this example             |
|            |                            | "pete-client-1"                                      |
+------------+----------------------------+------------------------------------------------------+
| username   | <user-id>                  | The user id - in this example                        |
|            |                            | "otadmin@otds.admin"                                 |
+------------+----------------------------+------------------------------------------------------+
| password   | <password>                 | The password for the user to authenticated           |
+------------+----------------------------+------------------------------------------------------+
| scope      | "resource:"<resource+name> | The scope is tied to a single resource, and          |
|            |                            | is formatted as "resource:<resource+name>",          |
|            |                            | where the <resource+name> is the name of             |
|            |                            | your resource, but with any spaces replaced with "+" |
+------------+----------------------------+------------------------------------------------------+

Important: If your resource name contains spaces, you must replace them with the '+' character. That is, for example scope=resource:My Resource becomes scope=resource:My+Resource.

Here is an example request:

POST http://otds.myhost.com:8080/otdsws/login HTTP/1.1
Accept: application/x-www-form-urlencoded
Content-Type: application/x-www-form-urlencoded
User-Agent: Jersey/2.19 (HttpUrlConnection 1.8.0_45)
Host: otds.myhost.com:8080
Connection: keep-alive
Content-Length: 133

password=XXX&grant_type=password&scope=resource%3Apete-test-server-1&client_id=pete-client-1&username=otadmin%40otds.admin

If the request is granted, you'll see a response containing the following attributes delivered as a JSON object:

+---------------+-------------------------------------------------------------+
| Name          | Notes                                                       |
+---------------+-------------------------------------------------------------+
| access_token  | The OAuth 2.0 Access Token                                  |
+---------------+-------------------------------------------------------------+
| expires_in    | The expiry time of the granted token, in seconds            |
+---------------+-------------------------------------------------------------+
| refresh_token | Used to request a new access_token (e.g. once the delivered |
|               | token has expired or is about to expire)                    |
+---------------+-------------------------------------------------------------+
| token_type    | Always "Bearer" for this flow                               |
+---------------+-------------------------------------------------------------+

Here is an example response:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
Date: Fri, 07 Aug 2015 15:43:02 GMT

3ea
{"token_type":"Bearer","expires_in":3600,"refresh_token":"ABQkq...","access_token":"ABSrk..."}
0

Using the Access Token to gain access to a Resource API

When a client calls a REST endpoint, it should include the delivered access token in a standard HTTP Authorization header of the form Authorization: Bearer <access-token>. Here is an example request:

GET http://poliver-e6520:8080/myresource/webresources/rest HTTP/1.1
Accept: application/json
Authorization: Bearer ABTYWpGvDKHyv5KfC<snip>Gv6wdbDo
User-Agent: Jersey/2.19 (HttpUrlConnection 1.8.0_51)
Host: poliver-e6520:8080
Connection: keep-alive

Verifying the Access Token from the Resource Server

In the steps above, your client requested an access token for a particular resource. When the client makes an API request it will deliver the access token within an Authorization header as shown above.

When the resource was registered with OTDS, it was assigned a resource identifier. When your resource server wishes to verify the access token, it must submit both the granted access token and resource identifier, as a JSON object, to OTDS for validation to the OTDS REST endpoint http://localhost:8080/otdsws/v1/authentication/tokeninfo, as shown in this example request:

POST http://localhost:8080/otdsws/v1/authentication/oauth/tokeninfo HTTP/1.1
Accept: application/json
Content-Type: application/json
User-Agent: Jersey/2.5.1 (HttpUrlConnection 1.8.0_51)
Cache-Control: no-cache
Pragma: no-cache
Host: localhost:8080
Connection: keep-alive
Content-Length: 505

{"token":"ABTYWpG<snip>6wdbDo","resource_id":"60832916-a568-4980-99da-f0338f899224"}

If the access token is valid, OTDS will respond with various details about the user account to which the token was constructed for. If the token is invalid, for a different resource, or expired, OTDS will respond accordingly with a descriptive error message.

Here is an example JSON response:

{
  "user": {
    "user_partition_id": "otds.admin",
    "name": "otadmin",
    "location": "cn=otadmin,ou=Root,ou=otds.admin,ou=IdentityProviders,dc=identity,dc=opentext,dc=net",
    "id": "otadmin@otds.admin",
    "description": "OpenText Administrator",
    "values": [
      {
        "name": "schemaType",
        "values": [
          4
        ]
      },
      {
        "name": "oTDomainName",
        "values": [
          "otds.admin"
        ]
      },
      {
        "name": "oTExternalID4",
        "values": [
          "OTDSADMIN\\otadmin"
        ]
      },
      {
        "name": "oTExternalID3",
        "values": [
          "otadmin@otds.admin"
        ]
      },
      {
        "name": "oTExternalID2",
        "values": [
          "otadmin@otds.admin"
        ]
      },
      {
        "name": "oTNetBIOSName",
        "values": [
          "OTDSADMIN"
        ]
      },
      {
        "name": "oTExternalID1",
        "values": [
          "Admin"
        ]
      },
      {
        "name": "oTMemberOf",
        "values": [
          "cn=otadmins,ou=Root,ou=otds.admin,ou=IdentityProviders,dc=identity,dc=opentext,dc=net",
          "cn=otagadmins,ou=Root,ou=otag-Prod,ou=IdentityProviders,dc=identity,dc=opentext,dc=net",
          "cn=otagadmins,ou=Root,ou=otag-Dev1,ou=IdentityProviders,dc=identity,dc=opentext,dc=net",
          "cn=otagadmins,ou=Root,ou=otagaw3,ou=IdentityProviders,dc=identity,dc=opentext,dc=net",
          "cn=otagadmins,ou=Root,ou=otaggw4,ou=IdentityProviders,dc=identity,dc=opentext,dc=net",
          "cn=otagadmins,ou=Root,ou=otagmbpm,ou=IdentityProviders,dc=identity,dc=opentext,dc=net",
          "cn=otagadmins,ou=Root,ou=otagawgdemo,ou=IdentityProviders,dc=identity,dc=opentext,dc=net"
        ]
      },
      {
        "name": "sn",
        "values": [
          "N/A"
        ]
      },
      {
        "name": "cn",
        "values": [
          "otadmin"
        ]
      },
      {
        "name": "description",
        "values": [
          "OpenText Administrator"
        ]
      },
      {
        "name": "entryDN",
        "values": [
          "cn=otadmin,ou=Root,ou=otds.admin,ou=IdentityProviders,dc=identity,dc=opentext,dc=net"
        ]
      },
      {
        "name": "UserMustChangePasswordAtNextSignIn",
        "values": [
          "false"
        ]
      },
      {
        "name": "UserCannotChangePassword",
        "values": [
          "false"
        ]
      },
      {
        "name": "PasswordNeverExpires",
        "values": [
          "true"
        ]
      },
      {
        "name": "accountDisabled",
        "values": [
          "false"
        ]
      },
      {
        "name": "accountLockedOut",
        "values": [
          "false"
        ]
      }
    ],
    "object_class": "oTPerson",
    "custom_attributes": null,
    "url_location": "cn=otadmin,ou=Root,ou=otds.admin,ou=IdentityProviders,dc=identity,dc=opentext,dc=net",
    "url_id": "otadmin@otds.admin"
  },
  "expiry_time": "2015-08-07T18:09:50Z",
  "token_id": "5e8ba534-c661-4c91-8cd3-e1c91e36db25",
  "scopes": [
    "resource:pete-test-server-1"
  ],
  "data_map": {
    "OTDS_HAS_PASSWORD": "true"
  }
}

The following table lists the expected HTTP status codes and reason.

+-------------+-----------------------------------------------------------------------------+
| HTTP Status | Notes                                                                       |
+-------------+-----------------------------------------------------------------------------+
|     200     | OK - Token info is returned                                                 |
+-------------+-----------------------------------------------------------------------------+
|     401     | User does not have access to the specified resource, or error parsing token |
+-------------+-----------------------------------------------------------------------------+
|     410     | Expired or revoked token                                                    |
+-------------+-----------------------------------------------------------------------------+
|     400     | Invalid resource ID                                                         |
+-------------+-----------------------------------------------------------------------------+

Refreshing an Access Token

At any time the client may request a new access token by posting the refresh token back to the OTDS OAuth token endpoint http://localhost:8080/otdsws/login along with the originally specified client id and grant type of refresh_token.

+---------------+----------------------------+---------------------------------------------------+
| Name          | Value                      | Notes                                             |
+---------------+----------------------------+---------------------------------------------------+
| grant_type    | "refresh_token"            | Informs the OAuth server that this request        |
|               |                            | is for a token refresh                            |
|               |                            | Grant                                             |
+---------------+----------------------------+---------------------------------------------------+
| client_id     | <client-id>                | The name of the client - in this example          |
|               |                            | "pete-client-1"                                   |
+---------------+----------------------------+---------------------------------------------------+
| refresh_token | <refresh-token>            | The refresh token                                 |
+---------------+----------------------------+---------------------------------------------------+

It is suggested that your client does this pre-emptively, rather than in response to an authentication error from the resource. In fact, many client-side OAuth frameworks will automatically do this for you.

Here is an example request:

POST http://localhost:8080/otdsws/login HTTP/1.1
Accept: application/x-www-form-urlencoded
Content-Type: application/x-www-form-urlencoded
User-Agent: Jersey/2.19 (HttpUrlConnection 1.8.0_51)
Host: localhost:8080
Connection: keep-alive
Content-Length: 547

refresh_token=ABRT0Eh<snip>PSgZvnGA*&grant_type=refresh_token&client_id=pete-client-1

And here is an example response

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
Date: Fri, 07 Aug 2015 18:25:07 GMT

21f
{"token_type":"Bearer","expires_in":3600,"access_token":"ABQ96H4xDG<snip>yl1_HKuIM*"}
0

Revoking Refresh and Access Tokens

You can revoke an access token token by issuing a DELETE to the OTDS server against the URL http://localhost:8080/otdsws/v1/authentication/oauth/<token> .

Here is an example request:

DELETE http://localhost:8080/otdsws/v1/authentication/oauth/ABTbx9R3<snip>CQUf_79al14N HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 0
Content-Type: application/json

Which produces this example response:

HTTP/1.1 204 No Content
Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: http://localhost:8080
Access-Control-Allow-Methods: DELETE, HEAD, GET, OPTIONS, POST, PUT
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 1728000
Content-Type: application/json
Date: Fri, 07 Aug 2015 18:36:55 GMT

When OTDS is requested to revoke an access token in this manner, any associated refresh token will also be revoked.

About OpenText

OpenText is the leader in Enterprise Information Management, helping customers to create a Digital-First World by simplifying, transforming, and accelerating their information needs. Over 100,000 customers already use OpenText solutions, either on premises or in our cloud. For more information about OpenText (NASDAQ: OTEX; TSX: OTC), please visit: www.opentext.com.

About the Author

Pete Oliver is a long standing employee of OpenText, occupying the position of Senior Software Architect. Pete has worked on various OpenText products and platforms, including ECM Collaboration, OpenText Directory Services (OTDS), AppWorks Developer, and more recently AppWorks Mobile.

2 Comments

0

Dear Pete Oliver,

Is there any possible way to do Two factor authentication using SMS in OTDS?.

Thanks,

Nithil


0

Hello Pete Oliver/Everyone,
Is it possible to implement 'Authorization Code Grant' flow using OTDS. Please suggest.

Thanks and Regards,
Tushar


Table of Contents

Your comment

To leave a comment, please sign in.