OAuth 2 Authorization Code grant in ACS - nordvall/TokenClient GitHub Wiki

Overview

Also see OAuth 2 in ACS.

ACS has support for playing the Authorization Server role in the OAuth 2 Authorization code flow. Because ACS has no knowledge of your users passwords and what data to approve access to, you need to implement the Resource Server parts in your own application.

When users give permission for an app, the Resource Server register a so called Delegation in ACS. The delegation consist of:

  • The user id and identity provider of the user that has given permission
  • The Client that is given permission to
  • The Resource Server given permission to
  • Optionally, an arbitrary permission string, mostly called "scope" in OAuth

After the delegation is in place ACS can generate authorization codes that your Resource Server hand off to your clients. The clients then procees with the authorization codes as a normal OAuth flow.

Prerequisities

  • The resource server needs to be configured for federated authentication with ACS.
  • The resource server needs to have a Management Credetial registered in ACS, to be able to register its clients and delegations.
  • The Client needs to be registered as a Service Identity in ACS, and have these attributes set:
    • The name
    • The redirect url
    • A password

When a user approves permission, you need to obtain:

  • The nameidentifier claim of the user
  • The identityprovider claim

...as they are needed to register the delegation. They can be obtained from the logged in user's claims.

For information about obtaining management credentials and registering clients, see ACS administration.

Running the flow

1. Request permissions from the Resource Owner

The Resource Server need to implement an OAuth 2 authorization endpoint in its web site. The endpoint address is, according to the OAuth specification, in following form: ?response_type=code&client_id=abc&redirect_uri=def&scope=ghi

Here are the steps that needs to be performed when a user arrives at the endpoint:

1.1 Resource Server: Ensure that the Resource Owner is logged in

This is done by your ordinary login mechanism.

1.2 Resource Server: Check if the delegation is already created in ACS

The delegation needs to have these attribues:

  • user: the logged in user id
  • identity provider: the identity provider claim of the logged in user
  • client: the Int64 id of the client found in client_id parameter
  • resource: the Int64 id of the Resource Server, as registered under Relying Parties in ACS
  • Permission: the permission level found in the scope parameter.
1.2.1 Look up the id of the Client

To be able to find this out, first you need to get the ACS Int64 Id of the Client:

Request:

GET /v2/mgmt/service/ServiceIdentities?$filter=Name eq 'clientname' HTTP/1.1
Authorization: Bearer *token*
Host: xxx.accesscontrol.windows.net
Accept: application/json

Parameters

parameter value example
Name The name of the Client, as registered under Service Identities in ACS ToDoApp

Response:

HTTP/1.1 200 OK

{ "d" : { "results": [
    { "Id": "20628923", "Name": "clientname", "Description": "test", "RedirectAddress": http://client.com,  "SystemReserved": false, "Version": "AAAAAAAvj4c=" }
    ] } }

The ServiceIdentityId is 20628923.

1.2.2 Look up the id of the Resource Server

Then you need to get the ACS Int64 Id of the Resource:

Request:

GET /v2/mgmt/service/RelyingParties?$filter=Name eq 'ResourceName' HTTP/1.1
Authorization: Bearer *token*
Host: xxx.accesscontrol.windows.net
Accept: application/json

Parameters

parameter value example
Name The name of the Resource Server, as registered under Relying partyies in ACS. ToDoSite

Response:

HTTP/1.1 200 OK

{ "d" : {"results": [
        { "Id": "20629044", "Name": "ToDoApp", "DisplayName": null, "Description": null, "TokenType": "JWT", "TokenLifetime": 600, "AsymmetricTokenEncryptionRequired": false, "SystemReserved": false }
    ] } }

The RelyingPartyId is 20629044.

1.2.3 Look for an existing delegation

Now you can query for the delegation:

GET /v2/mgmt/service/Delegations?$filter=RelyingPartyId eq 20629044 and ServiceIdentityId eq 20628923 and NameIdentifier eq 'username' and IdentityProvider eq 'identityrealm' and Permissions eq 'Read' HTTP/1.1
Authorization: Bearer *token*
Host: xxx.accesscontrol.windows.net
Accept: application/json

Parameters

parameter value example
RelyingPartyId The Int64 id of the Resource server, as obtained above. 20629044
ServiceIdentityId The Int64 id of the Client, as obtained above. 20628923
NameIdentifier The claims user name of the user. You can find it in the nameidentifier claim of the logged in user. [email protected]
IdentityProvider The identityprovider claim of the currently logged in user http://adfs.exampe.com/some/uri
Permissions The permission level that the Client asks for. You find it in the "scope" query string value Read

Delegation exists:

HTTP/1.1 200 OK

{ "d" : { "results": [
    { "Id": "20000164", "ServiceIdentityId": "20628923", "RelyingPartyId": "20629044", "IdentityProvider": "identityrealm", "NameIdentifier": "username", "Permissions": "Read", "AuthorizationCode": "YvKqykniq2hj7hxhp7pFjQ==" }
    ] } } 

The Authorization code is YvKqykniq2hj7hxhp7pFjQ==

Delegation does not exist:

HTTP/1.1 200 OK

{ "d" : { "results": [] } }

1.3 Resource Server: Ask the user for permission

This step needs to be performed if there is no delegation registered in ACS.

This is done by showing the Resource Owner an UI dialog that ask him to give permission for the Client to access the data.

1.4 Resource Server: Create the delegation

This is done if the Resource owner approves the permission.

The Resource Server then registers the delegation in ACS.

Request:

POST /v2/mgmt/service/Delegations HTTP/1.1
Authorization: Bearer *token*
Host: xxx.accesscontrol.windows.net
Accept: application/json
Content-Type: application/json

{ "ServiceIdentityId": "20628923", "RelyingPartyId": "20629044", "IdentityProvider": "identityrealm", "NameIdentifier": "username", "Permissions": "Read" }

Parameters:

See under "1.2.3 Look for an existing delegation"

Response:

HTTP/1.1 201 Created

{ "d" : { "Id": "20000171", "ServiceIdentityId": "20628923", "RelyingPartyId": "20629044", "IdentityProvider": "identityrealm", "NameIdentifier": "username", "Permissions": "Read", "AuthorizationCode": "OBt8lExB8ZMMXsonQi6hRg==" } } }

The authorization code is OBt8lExB8ZMMXsonQi6hRg==

2 Return authorization code to Client

Now the Resource Server can redirect the user to the address found in redirect_uri, with the UrlEncoded Authorization Code appended. By OAuth definition the url is in the following format:

?code=OBt8lExB8ZMMXsonQi6hRg%3D%3D

3 Client: Ask ACS for an access token

The client extracts the access code from the returned URL and builds the following token request:

POST /v2/oauth2-13/ HTTP/1.1
Host: xxx.accesscontrol.windows.net
Content-Type: application/x-www-form-urlencoded
Accept: application/json
Content-Length: 193

grant_type=authorization_code&client_id=abc&client_secret=123&redirect_uri=http://myapp.com&code=OBt8lExB8ZMMXsonQi6hRg%3D%3D&scope=http%3A%2F%2Ftodoapp.com%2F

Parameters:

Name Value Example
grant_type the OAuth 2 grant type Always authorization_code in this case.
client_id the ID of the Client, registered as Service Identity in ACS user1
client_secret One of the passwords of the client, registered for the Service Identity in ACS pasword1
redirect_uri The url to the Client application, as registered in the redirect_uri attribute of the Service Identity in ACS http://myapp.com
scope the application you need an access token for http://todoapp.com

Response:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
    "token_type":"urn:ietf:params:oauth:token-type:jwt",
    "access_token":"*token*",
    "expires_in":"599",
    "refresh_token":"y8Bi38tP6h5u14pdu5zaZg==",
    "scope":"http://todoapp.com/"
}

Now you can grab the access_token and use it for 599 seconds. After that, you can use the refresh_token to get another access token.

4. Client: Use the refresh token to renew the access token

Request:

POST /v2/oauth2-13/ HTTP/1.1
Host: xxx.accesscontrol.windows.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 193

grant_type=refresh_token&client_id=abc&client_secret=123&redirect_uri=http://myapp.com&refresh_token=y8Bi38tP6h5u14pdu5zaZg%3D%3D&scope=http%3A%2F%2Ftodoapp.com%2F

Parameters:

Parameter Value Example
grant_type the OAuth 2 grant type Always refresh_token in this case
client_id the id of the Client, as registered under ServiceIdentities in ACS user1
client_secret one of the valid passwords of the Client, must match the client of the original token request password1
redirect_uri the redirect uri of the client, as registered for the Client in ACS http://myapp.com
refresh_token the refresh token you received when you got your first access token 897bcdiuy8327
scope the application you need an access token to, must match the scope from the initial token request http://todoapp.com

Response:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
    "token_type":"urn:ietf:params:oauth:token-type:jwt",
    "access_token":"*token*",
    "expires_in":"599",
    "scope":"http://todoapp.com/"
}

Note that there is no new refresh token supplied this time, but you can continue to use the old refresh token multiple times.

External references