The UAA is a web application that manages users and Oauth 2 clients and issues tokens that are used for authorization. The UAA implements the Oauth 2 authorization framework and issues JSON web tokens. There is often a level of confusion of what the UAA does when initially starting to use it.
This document is intended a brief introduction to clear up some concepts for those that are new to Oauth 2 and/or the UAA.
Easiest way to explain what a token contains and how it is used is to get you to look at one. This step requires that you have Java 1.7 or higher installed.
git clone https://github.com/cloudfoundry/uaa.git
cd uaa
./gradlew run
You now have a UAA server running. There is a Ruby gem called cf-uaac, that one can use to communicate with the UAA.
But for sake of clarity, we will use curl
commands.
curl -v -d"username=marissa&password=koala&client_id=app&grant_type=password" -u "app:appclientsecret" http://localhost:8080/uaa/oauth/token
This yields a return token
{
"access_token":"eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJiYzNlNzQ1Ni05MWY1LTQ5NjEtYjg4ZC1kYjcwNTYyNmJhNzciLCJzdWIiOiI3Zjc5MWVhOS05OWI5LTQyM2QtOTg4Yi05MzFmMDIyMmE3OWYiLCJzY29wZSI6WyJjbG91ZF9jb250cm9sbGVyLnJlYWQiLCJjbG91ZF9jb250cm9sbGVyLndyaXRlIiwib3BlbmlkIiwicGFzc3dvcmQud3JpdGUiLCJzY2ltLnVzZXJpZHMiXSwiY2xpZW50X2lkIjoiYXBwIiwiY2lkIjoiYXBwIiwiZ3JhbnRfdHlwZSI6InBhc3N3b3JkIiwidXNlcl9pZCI6IjdmNzkxZWE5LTk5YjktNDIzZC05ODhiLTkzMWYwMjIyYTc5ZiIsInVzZXJfbmFtZSI6Im1hcmlzc2EiLCJlbWFpbCI6Im1hcmlzc2FAdGVzdC5vcmciLCJpYXQiOjE0MDY1Njg5MzUsImV4cCI6MTQwNjYxMjEzNSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3VhYS9vYXV0aC90b2tlbiIsImF1ZCI6WyJzY2ltIiwib3BlbmlkIiwiY2xvdWRfY29udHJvbGxlciIsInBhc3N3b3JkIl19.ZOhp7HmYF0ufvxXrkut40eHZbHFzAb5EETT2NL7n2Cs",
"token_type":"bearer",
"refresh_token":"eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJmYzEwZjVjZC1mODY2LTQzY2MtYTQ4ZS04ZDE3NmY2OGM1MTEiLCJzdWIiOiI3Zjc5MWVhOS05OWI5LTQyM2QtOTg4Yi05MzFmMDIyMmE3OWYiLCJzY29wZSI6WyJjbG91ZF9jb250cm9sbGVyLnJlYWQiLCJjbG91ZF9jb250cm9sbGVyLndyaXRlIiwib3BlbmlkIiwicGFzc3dvcmQud3JpdGUiLCJzY2ltLnVzZXJpZHMiXSwiaWF0IjoxNDA2NTY4OTM1LCJleHAiOjE0MDkxNjA5MzUsImNpZCI6ImFwcCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC91YWEvb2F1dGgvdG9rZW4iLCJncmFudF90eXBlIjoicGFzc3dvcmQiLCJ1c2VyX25hbWUiOiJtYXJpc3NhIiwidXNlcl9pZCI6IjdmNzkxZWE5LTk5YjktNDIzZC05ODhiLTkzMWYwMjIyYTc5ZiIsImF1ZCI6WyJjbG91ZF9jb250cm9sbGVyLnJlYWQiLCJjbG91ZF9jb250cm9sbGVyLndyaXRlIiwib3BlbmlkIiwicGFzc3dvcmQud3JpdGUiLCJzY2ltLnVzZXJpZHMiXX0.MWNTyXvGU4YgEFqXToO-D_HplWjfSK0xxqVQc7FYKZg",
"expires_in":43199,
"scope":"cloud_controller.read cloud_controller.write openid password.write scim.userids",
"jti":"bc3e7456-91f5-4961-b88d-db705626ba77"
}
and the actual value that we are interested is
eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJiYzNlNzQ1Ni05MWY1LTQ5NjEtYjg4ZC1kYjcwNTYyNmJhNzciLCJzdWIiOiI3Zjc5MWVhOS05OWI5LTQyM2QtOTg4Yi05MzFmMDIyMmE3OWYiLCJzY29wZSI6WyJjbG91ZF9jb250cm9sbGVyLnJlYWQiLCJjbG91ZF9jb250cm9sbGVyLndyaXRlIiwib3BlbmlkIiwicGFzc3dvcmQud3JpdGUiLCJzY2ltLnVzZXJpZHMiXSwiY2xpZW50X2lkIjoiYXBwIiwiY2lkIjoiYXBwIiwiZ3JhbnRfdHlwZSI6InBhc3N3b3JkIiwidXNlcl9pZCI6IjdmNzkxZWE5LTk5YjktNDIzZC05ODhiLTkzMWYwMjIyYTc5ZiIsInVzZXJfbmFtZSI6Im1hcmlzc2EiLCJlbWFpbCI6Im1hcmlzc2FAdGVzdC5vcmciLCJpYXQiOjE0MDY1Njg5MzUsImV4cCI6MTQwNjYxMjEzNSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3VhYS9vYXV0aC90b2tlbiIsImF1ZCI6WyJzY2ltIiwib3BlbmlkIiwiY2xvdWRfY29udHJvbGxlciIsInBhc3N3b3JkIl19.ZOhp7HmYF0ufvxXrkut40eHZbHFzAb5EETT2NL7n2Cs
You can look at the body of the access token using a decoder. More on Tokens can be found here
{
"exp": 1406612135,
"user_id": "7f791ea9-99b9-423d-988b-931f0222a79f",
"sub": "7f791ea9-99b9-423d-988b-931f0222a79f",
"cid": "app",
"iss": "http://localhost:8080/uaa/oauth/token",
"jti": "bc3e7456-91f5-4961-b88d-db705626ba77",
"client_id": "app",
"iat": 1406568935,
"scope": [
"cloud_controller.read",
"cloud_controller.write",
"openid",
"password.write",
"scim.userids"
],
"grant_type": "password",
"user_name": "marissa",
"email": "marissa@test.org",
"aud": [
"scim",
"openid",
"cloud_controller",
"password"
]
}
Some of these fields are described in the JSON web tokens specification. However, the vendor may add additional fields, or attributes, to the token itself.
There are some notable fields here that we are interested in:
- user_id - a UUID for the user
- cid/client_id - unique name for the client. Unique to the system it runs on.
- scope - a list of permissions that this client has on behalf of this user
- aud - the audience, who this token is intended for.
A user is often represented as a live person, or a process running.
A client is an application that acts on behalf of a user or act on its own.
A resource server is often defined as an application with access to a user's data
A brief and informative tutorial has already been written.
An access token can be requested in four different ways, in the Oauth specification they are referred to as grant types
- client_credentials - no user involved. requesting a token to represent a client only
- password - the client uses the user's credentials and passes them to the UAA to generate a token This is the method we used in our example.
- implicit - this is similar to the password grant, but a client password(secret) is not needed
- authorization_code - in this scenario, the client never sees the user's credentials. It is the most secure grant type but relies on 302 redirects from the HTTP protocol.
When it comes to the UAA, and integrating with the UAA, you will be dealing with scopes. Scopes are essentially permissions, and are added as a named parameter in the access token.
In the Java world, often referred to as roles. Scopes in a token have two different names in the UAA token
- scope - when the token represents a client acting on behalf of a user
- authorities - when the token represents the client (application) itself
This is how the permissions are labeled in the token by the UAA. When a resource server receives a request containing a token, the server can make a decision based on the scopes in the token. The resource MUST validate the token first, there are several ways of doing this discussed in the Oauth 2 specification.
The resource server must differentiate between a client only token, and read 'authorities', or a client on behalf of user token to read the 'scope' field.
The name of the scope, such as password.write
are arbitrary strings. They don't mean anything to any component except
the resource server that uses them for authorization. For example, password.write
is a scope that must be present
in the token when a request to the UAA is made for a password change.
Scopes, are arbitrary strings, defined by the client itself. The UAA does use the base name of a scope and adds it to the audience field. The 'base name' is defined as anything before the last dot.
In the UAA each client has a list of client authorities
. This is List<String>
of scopes
that represents the permissions the client has by itself. The second field the client has is the scopes
field.
The client scopes
represents the permissions that the client uses when acting on behalf of a user.
Authorities are used when we have the client_credentials
grant type
In the UAA, a user belongs to one or more groups. A group in UAA, represents a scope in the Oauth world. Groups can be nested allowing easier management of group memberships.
When a token is requested by a client on behalf of a user the following process is followed
- The client is authenticated
- The user is authenticated
- The client scopes are retrieved
- The user scopes are retrieved
- A scope list is created with the shared scopes from step 3 and 4.
- A token is generated, with the scope parameter from step 5.
As scopes are arbitrary strings and those strings often containing dots to create a naming conventions. For example, a resource server maintaining documents could create the following naming scheme
document.<document id>.read
document.<document id>.write
document.<document id>.delete
A client that is accessing the resource server and reading, writing and deleting documents on behalf of a user, can be
assigned the scope=document.*.read document.*.delete
. You can now assign scopes to the user in
the form of
document.asdsd-adasda-123212.write
document.asdsd-adasda-123212.read
document.wqere-adasda-adasda.read
document.wqere-adasda-adasda.delete
The token representing the user's permission, would contain
document.asdsd-adasda-123212.read
document.wqere-adasda-adasda.read
document.wqere-adasda-adasda.delete
since the client does not have the write
.
The audience field for the token would be document
.
A user may not have a wild card in the scope (group name). In that case, the star, *, does not represent a wildcard, it's just another character in the arbitrary strings. Scope names are case sensitive.
Wild card scopes have been available since UAA version 1.8.0.
UaaTokenEnhancer
is an interface which can be used to enhance access and refresh tokens with custom attributes. You may plug-in a UaaTokenEnhancer
into the bean UaaTokenServices
. Values returned by methods
getExternalAttributes
enhance
will be passed to setAdditionalInformation
of the token object.
The interface method enhance
is the successor of getExternalAttributes
and can be used as replacement.