OAuth2 Resource Owner Password Credential Grant with 3Scale and Red Hat SSO

Cobblestone pattern / Chris Waits

This blog post continues demonstrating (and documenting) the use of the OAuth2 + OIDC Debugger with 3Scale API Management and Red Hat SSO. Now, we are going to look at the OAuth2 Resource Owner Password Credential Grant with 3Scale and Red Hat SSO.

The theory and details of OAuth2 are described here.

The Resource Owner Password Credential Grant involves the Client application asking for the username and password directly from the end-user rather than directing the user to a login page hosted by the Authorization Server (or other Identity Provider) like in the first two Grants. The Client then passes the client credentials (client identifier + client secret in the case of Confidential Client or just client identifier in the case of a Public Client) and end-user credentials to the Authorization Server

To test the Resource Owner Password Credential Grant, do the following.

  • Follow the instructions to setup Red Hat SSO, 3Scale API Management, APICast, and the OAuth2 + OIDC Debugger here. You don’t have to test the OAuth2 Authorization Code Grant and API call at the bottom. Obviously, there are many details in that post. Unfortunately, the setup for these tools is non-trivial; so, many details.
  • Make sure that the Authorization Code Grant example in the link above works before proceeding.
  • Open a browser and go to “http://localhost:3000”. You will see something similar to the following.


  • Choose OAuth2 Resource Owner Credential Grant from the drop down menu.
  • You will see something similar to the following.


  • Under “Enter Endpoints”, enter the Token Endpoint value. For Red Hat SSO v7.1, this will be something like:
http://localhost:8080/authorize
  • The Authorization Endpoint is not used with this grant.
  • Enter the client identifier into the Client ID field (this value is managed by 3Scale and will be unique to your environment).
  • Enter the client secret into the Client Secret field (this value is managed by 3Scale and will be unique to your environment).
  • Enter the Redirect URL into the Redirect URL field (this will be http://localhost:3000/callback if you followed the instructions in the last blog).
  • Enter the scope into the Scope field (use “openid profile User).
  • Enter the username into the Username field (User1 if you followed the instructions in the last blog).
  • Enter the password into the password field (use “password123” if you followed the instructions in the last blog).
  • No resource parameter is needed in this example. If you were to add one, it would have to match the client identifier above.
  • If your IdP is using a self-signed certificate or an internal CA for the TLS listening endpoint, click No for the “SSL Certificate Validation?” question. If the instructions in these blog posts have been followed, then the APICast listening endpoint is non-SSL/TLS. If SSL/TLS is not being used, it doesn’t really matter if Yes or No is chosen.
  • Click the “Get Token” button.
  • Scroll down.
  • If everything goes as expected, you will get something similar to the following:


An access token, refresh token, and id_token are returned (assuming the password, client secret, and other details are correct).

The contents of the ID token payload looks like:

{
"jti": "246eea83-96e8-4f72-a7b3-59be88fa4861",
"exp": 1510589930,
"nbf": 0,
"iat": 1510586330,
"iss": "https://idp.levvel.io:8443/auth/realms/blog_demo",
"aud": "379fbeeb",
"sub": "71c904f1-e978-4542-80de-fa98ebb241e5",
"typ": "ID",
"azp": "379fbeeb",
"auth_time": 0,
"session_state": "8feefb58-124b-4a4c-976c-18a2d1ff6111",
"acr": "1",
"name": "",
"preferred_username": "user1"
}

The contents of the access token looks like:

{
"jti": "1008e6e4-f77a-45ce-9be6-ceea7f749066",
"exp": 1510589930,
"nbf": 0,
"iat": 1510586330,
"iss": "https://idp.levvel.io:8443/auth/realms/blog_demo",
"aud": "379fbeeb",
"sub": "71c904f1-e978-4542-80de-fa98ebb241e5",
"typ": "Bearer",
"azp": "379fbeeb",
"auth_time": 0,
"session_state": "8feefb58-124b-4a4c-976c-18a2d1ff6111",
"acr": "1",
"client_session": "52b9c508-5b7a-49d8-a9b6-1f64d636e182",
"allowed-origins": [
"*",
"http://localhost:3000"
],
"resource_access": {
"blog-post-demo-client-001": {
"roles": [
"User"
]
}
},
"name": "",
"preferred_username": "user1"
}

The contents of the refresh token looks like:

{
"jti": "bbb9504a-5f51-4682-bfad-2b657597cb23",
"exp": 1510588130,
"nbf": 0,
"iat": 1510586330,
"iss": "https://idp.levvel.io:8443/auth/realms/blog_demo",
"aud": "379fbeeb",
"sub": "71c904f1-e978-4542-80de-fa98ebb241e5",
"typ": "Refresh",
"azp": "379fbeeb",
"auth_time": 0,
"session_state": "8feefb58-124b-4a4c-976c-18a2d1ff6111",
"client_session": "52b9c508-5b7a-49d8-a9b6-1f64d636e182",
"resource_access": {
"blog-post-demo-client-001": {
"roles": [
"User"
]
}
}
}

Test API Call

Now that the user has been authenticated and has an access token, let’s make an API call.

Do the following.

  • Copy the Echo API Swagger document:
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Echo API",
"description": "A sample echo API"
},
"host": "echo-api.3scale.net",
"basePath": "/",
"schemes": [
"http"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"securityDefinitions": {
"oauth": {
"type": "oauth2",
"authorizationUrl": "http://localhost:8080/authorize",
"tokenUrl": "http://localhost:8080/oauth/token",
"flow": "accessCode",
"scopes": {
"openid profile User": "Perform user related functions"
}
}
},
"paths": {
"/": {
"get": {
"description": "Echo API with no parameters",
"operationId": "echo_no_params",
"produces": [
"application/json",
"application/xml",
"text/xml",
"text/html"
],
"parameters": [
{
"name": "user_key",
"in": "query",
"description": "Your API access key",
"required": true,
"x-data-threescale-name": "user_keys",
"type": "string"
}
],
"responses": {
"200": {
"description": "response",
"schema": {
"$ref": "#/definitions/ResponseModel"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/ErrorModel"
}
}
},
"security": [
{ "oauth": ["openid profile User"] }
]
}
},
"/{echo}": {
"get": {
"description": "Echo API with parameters",
"operationId": "echo_with_params",
"produces": [
"application/json",
"application/xml",
"text/xml",
"text/html"
],
"parameters": [
{
"name": "echo",
"in": "path",
"description": "The string to be echoed",
"required": true,
"type": "string"
},
{
"name": "user_key",
"in": "query",
"description": "Your API access key",
"required": true,
"x-data-threescale-name": "user_keys",
"type": "string"
}
],
"responses": {
"200": {
"description": "response",
"schema": {
"$ref": "#/definitions/ResponseModel"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/ErrorModel"
}
}
}
}
}
},
"definitions": {
"ResponseModel": {
"type": "object",
"required": [
"method",
"path",
"args",
"headers"
],
"properties": {
"method": {
"type": "string"
},
"path": {
"type": "string"
},
"args": {
"type": "string"
},
"headers": {
"type": "object"
}
}
},
"ErrorModel": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
}
}
}
}
}
  • Open http://editor.swagger.io in a browser tab.
  • Paste the swagger definition into the editor.
  • Give the editor a moment to refresh the current view.
  • Click on the “Switch back to previous editor” link. This gives a version of the Swagger Editor that just prompts for an access token rather than performing the whole OAuth2 protocol sequence again (we want to use the token that was just obtained).
  • You will see something similar to the following on the right-hand side.


  • Click the Authenticate button.


  • Enter the access token from the OAuth2 + OIDC Debugger “Access Token” field above.
  • Click the Authenticate button.
  • Scroll down to the ‘/’ Path on the right-hand pane.


  • Click the “Try this operation” button.


  • Click the “Send Request” button.


  • The actual response looks like
{
"method": "GET",
"path": "/",
"args": "",
"body": "",
"headers": {
"HTTP_VERSION": "HTTP/1.1",
"HTTP_HOST": "echo-api.3scale.net",
"HTTP_ACCEPT": "application/json",
"HTTP_ACCEPT_ENCODING": "gzip, deflate, sdch, br",
"HTTP_ACCEPT_LANGUAGE": "en-US,en;q=0.8,fa;q=0.6,sv;q=0.4",
"HTTP_AUTHORIZATION": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJoc1h6Ri1XaHNjRDNQakdfMHVBMW9yYmhob3RuYmRMQTJVQ1BLMWtNQ25RIn0.edyJqdGkiddOiJjY2dY5MzM4Zi02dYThjdLTQyNmdMtOGdJiZC0yYdWIxYjU1ZmdM5ZmYiLCJldeHAiOjE1MTEyOTIwNTUsIm5iZiI6MCwiaWF0IjoxNTExMjg4NDU1LCJdpc3MiOiJoddHRwczovL2VjMi01Mi03Mi03MS0yMjkuY29tcHV0ZS0xLmFtYXpdvbmF3cyd5jb206ODQ0My9hdXRoL3JlYWxtcy9kZW1vX3Byb2plY3Rdfc2YdiLCJhdWQiOiIzNzlmdYmVlYidIsInN1YiId6IjQzMd2U3MTgyLWFhOTgtNDAyYy1iN2NmLTM1ZTZhYjhmYTkyMiIsInR5cCI6IkJlYXJlciIsdImF6cCI6IjM3OWZiZWViIiwibm9uY2UiOdiIwZDcyZDddMi0xZGJmLTd2NjMtODd2Ny0zYdmUyNGZiZmIzMDMiLCJhdXRoX3RpbWdiOjE1MTEyODg0NDgsInNlc3Npb25fc3RhdGUiOiI5NjU1ZWQ5Mi1mNGU1LTRiNWMtdGYwZS0zZjEyZmRlNzRkMDEiLCJhY3IiOiIxIiwiY2xpZW50X3Nlc3Npb24iOiJkNzExYzUzYS00NTllLTQwYTEtYjAzMy0wMzM2NjI0NjRmMGMiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cDovL2xvY2FsaG9zdDozMDAwIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImRlbW9fcHJvamVjdF9zZiI6eyJyb2xlcyI6WyJ0ZXN0ZXIiXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJ2aWV3LXByb2ZpbGUiXX19LCJuYW1lIjoidXNlciBvbmUiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ1c2VyMSIsImdpdmVuX25hbWUiOiJ1c2VyIiwiZmFtaWx5X25hbWUiOiJvbmUiLCJlbWFpbCI6InVzZXIxQGxldnZlbC5pbyJ9.vEotb2OEiVUsQA7taHadC6jM1UEGdzHpeGzxC__5d9eKpC1WAA424mp7_FZfSmcBEY8nRz6uFybDE4T_cYNg9y0uBg27VasZcnQVWpp68Ol6adnA9q3kjOBep46s2iBqc7PUzw0K6_QEps7mQhmt3C2tsWSD231XGZ27GnA7FIB_ScR7_9c5-PXci8lP5EX1z-uIH0rxRGTmyIulGsk7867KMS1qFF5KPUMPxrNbXeTH0rxEMPn3C1-uSyFirlG2g-YfVQXgBUQ6SxvHATYQrKGkCQ01udHkgicKT9vZgPn5atMkSme1Lx3Kg8H3-pM7GIGHdempm2QOKnSoVwxGlw",
"HTTP_ORIGIN": "http://editor2.swagger.io",
"HTTP_REFERER": "http://editor2.swagger.io/",
"HTTP_USER_AGENT": "Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
"HTTP_X_3SCALE_PROXY_SECRET_TOKEN": "Shared_secret_sent_from_proxy_to_API_backend_8256d9c54eedfd9c",
"HTTP_X_REAL_IP": "1.1.1.1",
"HTTP_X_FORWARDED_FOR": "2.2.2.2, 3.3.3.3",
"HTTP_X_FORWARDED_HOST": "echo-api.3scale.net",
"HTTP_X_FORWARDED_PORT": "443",
"HTTP_X_FORWARDED_PROTO": "https",
"HTTP_FORWARDED": "for=1.1.1.1;host=echo-api.3scale.net;proto=https"
},
"uuid": "f8577973-d330-44ce-a314-da3948b8335d"
}

That concludes this blog post.

Image: Cobblestone pattern / Chris Waits