Skip to content

Instantly share code, notes, and snippets.

@esfand
Last active August 22, 2023 10:44
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save esfand/6688095 to your computer and use it in GitHub Desktop.
Save esfand/6688095 to your computer and use it in GitHub Desktop.
UAA Security Features and Configuration

UAA Features and Configuration

It is the responsibility of a Resource Server to extract information about the user and client application from the access token and make an access decision based on that information.

This guide will help authors of resource Servers and maintainers of client and user account data to understand the range of information available and the kinds of decisions that can be taken.

The UAA itself is a Resource Server, so the access decisions taken by the UAA are used as an example.

User Accounts

Security Metadata

User accounts are either of type "user" or type "admin" (using the SCIM type field from the core schema). These translate into granted authorities, [uaa.user] or [uaa.admin,uaa.user] respectively, for the purposes of access decisions within the UAA (i.e. admin users also have the user role). Granted authorities are not directly visible to Resource Servers, but they show up as scopes in the access tokens.

Resource Servers may choose to use this information as part of an access decision, and this may be good enough for simple use cases (e.g. users belong to a small number of relatively static roles), but in general they will need to maintain their own acess decision data since roles on UAA don't necessarily correspond to the same thing on a Resource Server.

Support for SCIM groups is currently provided only through the authorities attribute of the user object. Resource Servers that are also SCIM clients can modify this attribute themselves, but it might be better (and safer) if the data don't change much to have an admin user or client do the role assignments. In any case it is recommended that Resource Servers have sensible defaults for new users that have not yet been assigned a role.

Bootstrap

There are 2 distinct scenarios:

  1. Demo or test with vanilla code and no special environment. A UAA service started with no active Spring profile will initialize a single user account (marissa/koala).

  2. A vcap environment: integration testing or in production. If the service starts with any active Spring profile, by default it will not touch the user database. The SCIM endpoints can be used to provision user accounts, once a client with the correct privileges has been registered.

In either case, additional user accounts and client registrations can be bootstrapped at start up by providing some data in uaa.yml. Example users:

scim:
  users:
    - paul|wombat|paul@test.org|Paul|Smith|uaa.admin
    - stefan|wallaby|stefan@test.org|Stefan|Schmidt

The format for the user is username|password|email|first_name|last_name(|comma-separated-authorities). Remember that authorities are represented as groups in SCIM.

Account lockout policy

In its default configuration, the UAA does not lock accounts permanently when a user repeatedly fails authentication. Instead it temporarily locks a user out for a short period (5 minutes by default) after 5 failed logins within the previous hour. The failure count is reset when a user successfully authenticates.

OAuth Client Applications

Security Metadata

Client application meta data can be used by Resource Servers to make an access decision, and by the Authorization Server (the UAA itself) to decide whether to grant an access token.

Scope values are arbitrary strings, but are a contract between a Client Application and a Resource Server, so in cases where UAA acts as a Resource Server there are some "standard" values (scim.read, scim.write, passsword.write, openid, etc.) whose usage and meaning is described below.

Scopes are used by the Authorization Server to deny a token requested for a scope not on the list, and should be used by a Resource Server to deny access to a resource if the token has insufficient scope.

UAA client applications have the following meta data (some are optional, but to prevent mistakes it is usually better to use a default value):

  • authorized-grant-types: a comma-separated list of OAuth2 grant types, as defined in the spec: choose from client_credentials, password, implicit, refresh_token, authorization_code. Used by the Authorization Server to deny a token grant if it is not on the list. If in doubt use authorization_code and refresh_token.
  • scope: a list of permitted scopes for this client to obtain on behalf of a user (so not relevant to client_credentials grants). Also used as the default scopes for a token where the client does not explicitly specify scopes in the authorization request.
  • authorities: a list of granted authorities for the client (e.g. uaa.admin or any valid scope value). The authorities are used to define the default scopes that are assigned to a token in a client_credentials grant, and to limit the legal values if explicit scopes are requested in that case.
  • secret: the shared secret used to authenticate token grant requests and token decoding operations (not revealed to Resource Server).
  • resource-ids: white list of resource ids to be included in the decoded tokens granted to this client. The UAA does not store any data here (it should be none for all clients), but instead creates a list of resource ids dynamically from the scope values when a token is granted. The resource id is extracted from a scope using a period separator (the last occurrence in the string) except for some standard values (e.g. openid) that are not controlled by the UAA or its own resources. So a scope of cloud_controller.read is assigned a resource id of cloud_controller, for instance.

Bootstrap

Client registration can be initialized by adding client details data to uua.yml. The UAA always starts with a registered admin client. There are 2 typical scenarios for additional client registration bootstraps:

  1. Demo or test with vanilla code and no custom uaa.yml. A UAA service started with no active Spring profile will start with some client registrations (used in samples to make the out-of-the box experience for new users as convenient as possible). More clients and user accounts will be created by the integration tests.

  2. A vcap environment: integration testing or in production. By default no clients are created if any Spring profile is active, but client registrations can be configured in uaa.yml and in some well-known situations clients this will happen. In particular, the dev_setup environment and the CF.com deployment job both start up with additional client registrations that are needed by the basic Cloud Foundry use cases (vmc and cloud_controller). If the vcap Spring profile is active in the integration tests, no additional accounts will be created.

Clients are bootstrapped from config if they are not present in the backend when the system starts up (i.e. once the system has started up once, config changes will not affect the client registrations for existing clients). Certain fields (e.g. secret) can be reset if the bootstrap component is configured to do so (it is not by default).

The admin client has the following properties (in the default uaa.yml always present on the classpath but overriddable by specifying all the values again in a custom config file):

  id: admin
  secret: adminsecret
  authorized-grant-types: client_credentials
  scope: none
  authorities: uaa.admin,clients.read,clients.write,clients.secret

The admin client can be used to bootstrap the system by adding additional clients. In particular, user accounts cannot be provisioned until a client with access to the scim resource is added.

Demo Environment

The default Spring profile initializes 3 clients in addition to the admin client, e.g. if the server is started from the command line after a fresh clone from github for demo purposes:

vmc:
  id: vmc
  authorized-grant-types: implicit
  scope: cloud_controller.read,cloud_controller.write,openid,password.write
  authorities: uaa.none
  resource-ids: none
app:
  id: app
  secret: appclientsecret
  authorized-grant-types: password,authorization_code,refresh_token
  scope: cloud_controller.read,cloud_controller.write,openid,password.write,tokens.read,tokens.write
  authorities: uaa.none
  resource-ids: none

VCAP Dev Setup

In dev_setup these client accounts (in addition to the admin client) are initialized:

cloud_controller:
  authorized-grant-types: client_credentials
  scope: none
  authorities: scim.read,scim.write,password.write,tokens.read,tokens.write
  id: cloud_controller
  secret: ...
  resource-ids: none
vmc:
  id: vmc
  authorized-grant-types: implicit
  scope: cloud_controller.read,cloud_controller.write,openid,password.write
  authorities: uaa.none
  resource-ids: none
  redirect-uri: http://uaa.cloudfoundry.com/redirect/vmc

The cloud controller secret is generated during the setup. The same clients are initialized in CF.com, but the secret is different. Additional clients can be added during start up using uaa.yml, e.g.

oauth:
  clients:
    vmc:
      authorized-grant-types: implicit
      scope: cloud_controller.read,cloud_controller.write,password.write,openid
      authorities: uaa.none
      id: vmc
      resource-ids: none
      redirect-uri: http://uaa.cloudfoundry.com/redirect/vmc

Token Scope Rules

Introduction

When a client application asks for a new access token it can optionally provide a set of requested scopes (space separated, e.g. scope=openid cloud_controller.read). The UAA will use that set if provided and that will be the scope of the token if granted. Otherwise, if no explicit value is requested, defaults will be supplied according to what the client and user are allowed to do. The rules governing the defaults and what is allowed are described next.

User Tokens

A token granted on behalf of a user (grant type anything except client_credentials) takes its default scopes from the scope field of the client registration. Whether or not the default values are used, the requested scopes are then validated:

  • The user's authorities (SCIM groups) are augmented with some static values, configurable but defaulting to [openid, cloud_controller.read, cloud_controller.write]
  • Allowed scopes consist of the intersection of the client scope and the augmented user authorities.
  • Disallowed scopes are removed from the request.
  • If all the requested scopes are disallowed then clients get a 400 response with a JSON error message indicating the allowed values (for implicit grants it should be a 302 according to the OAuth2 spec, but that change hasn't been implemented yet). The exception to that rule is for clients with no registered scopes (no error in that case), but there shouldn't be any such clients in a production system.

Note that the filtering of scopes by user authorities might mean that a client gets a narrower-scoped token than it originally asked for, e.g. if it asks for no scope=dash.admin dash.user openid, the token might come back with only dash.user openid. Tokens are opaque to client applications, so they have to be prepared for resource servers to deny access to some resources based on the scope of the token when it is presented.

Client Tokens

A token issued on a client_credentials grant has default and allowed scopes equal to the client authorities. Requesting a disallowed scope will result in a 400 reponse and an error message that indicates the allowed scopes. A client would normally take the default scopes when acting on its own behalf - since no approval is necessary there is no point narrowing the scope.

UAA Resources

Introduction

All OAuth2 protected resource have an id (as listed individually). Any request whose token does not have a matching resource id (aud field in decoded token) will be rejected. Resources that are not OAuth2 protected resources do not have a resource id (e.g. those with simple HTTP Basic authentication).

Token Management

Resource ID = tokens. Rules:

  • Revoke user token:
    • Token has scope uaa.admin, or
    • If token represents user, user is authenticated and is the owner of the token to be revoked, and token has scope tokens.write
  • List user tokens:
    • Token has scope uaa.admin or
    • If token represents user, user is authenticated and is the owner of the token to be read, and token has scope tokens.read
  • Revoke client token:
    • Token has scope uaa.admin or
    • Token represents the client in the token to be revoked, and token has scope tokens.write
  • List client tokens:
    • Token has scope uaa.admin or
    • Token represents the client in the token to be revoked, and token has scope tokens.read

Client Registration

Resource ID = clients. Rules:

  • Remove, update or add client registration
    • Token has scope clients.write
  • Inspect client registration
    • Token has scope clients.read

Client Secret Mangagement

Resource ID null (so all clients can change their password). Rule:

  • Change secret
    • Token has scope clients.secret
    • Either token has scope uaa.admin or client can only change its own secret
    • Either token has scope uaa.admin or client provides the old secret
    • Even if token has scope uaa.admin client must provide the old value to change its own secret

Password Change

Resource ID = password. Rules:

  • Change password
    • Token has scope password.write
    • If token represents a client, scope includes uaa.admin
    • If token represents a user, either scope includes uaa.admin or user provides the old password

User Account Management

Resource ID = scim. Rules:

  • List or search users

    • Token with scope scim.read provides read/query access to ALL users in the UAA
  • Delete, add user account

    • Token has scope scim.write
  • Update existing user account

    • Token with scope scim.write lets you update ANY user's information in the UAA

In addition, a User Token obtained by a client with authorities scim.me (eg. token from authorization_code or password grant flow) provides read/query/update access to that particular user's account.

Username from ID Queries

Resource ID = scim. Rules:

  • Obtain username information via /ids/Users
  • filter parameter must be supplied
  • Only attributes userName and id are returned (and can be queried on)

User Profiles

Used for Single Sign On ( OpenID Connect lite ). Resource ID = openid. Rules:

  • Obtain user profile data
    • Token has scope openid

Groups & Membership Management

Resource ID = scim. Rules:

  • List or Search groups

    • Token has scope scim.read
  • Delete or Add groups

    • Token has scope scim.write
  • Update group name or add/remove members

    • Token has either scim.write OR groups.update

User Account and Authentication

Document's Source

The UAA is the identity management service for Cloud Foundry. It's primary role is as an OAuth2 provider, issuing tokens for client applications to use when they act on behalf of Cloud Foundry users.

It can also authenticate users with their Cloud Foundry credentials, and can act as an SSO service using those credentials (or others). It has endpoints for managing user accounts and for registering OAuth2 clients, as well as various other management functions.

Quick Start

If this works you are in business:

$ git clone git://github.com/cloudfoundry/uaa.git
$ cd uaa
$ mvn install

Each module has a mvn tomcat:run target to run individually, or you could import them as projects into STS (use 2.8.0 or better if you can). The apps all work together the apps running on the same port (8080) as /uaa, /app and /api.

You will need Maven 3.0.4 or newer.

Deploy to Cloud Foundry

You can also build the app and push it to Cloud Foundry, e.g.

$ mvn package install
$ vmc push myuaa --path uaa/target

(If you do that, choose a unique application id, not 'myuaa'.)

Demo of command line usage on local server

First run the UAA server as described above:

$ cd uaa
$ mvn tomcat:run

Then start another terminal and from the project base directory, ask the login endpoint to tell you about the system:

$ curl -H "Accept: application/json" localhost:8080/uaa/login
{
  "timestamp":"2012-03-28T18:25:49+0100",
  "commit_id":"111274e",
  "prompts":{"username":["text","Username"],
    "password":["password","Password"]
  }
}

Then you can try logging in with the UAA ruby gem. Make sure you have ruby 1.9, then

$ gem install cf-uaac
$ uaac target http://localhost:8080/uaa
$ uaac token get marissa koala

(or leave out the username / password to be prompted).

This authenticates and obtains an access token from the server using the OAuth2 implicit grant, similar to the approach intended for a client like VMC. The token is stored in ~/.uaac.yml, so dig into that file and pull out the access token for your vmc target (or use --verbose on the login command line above to see it logged to your console).

Then you can login as a resource server and retrieve the token details:

$ uaac target http://localhost:8080/uaa
$ uaac token decode [token-value-from-above]

You should see your username and the client id of the original token grant on stdout, e.g.

  exp: 1355348409
  user_name: marissa
  scope: cloud_controller.read openid password.write scim.userids tokens.read tokens.write
  email: marissa@test.org
  aud: scim tokens openid cloud_controller password
  jti: ea2fac72-3f51-4c8f-a7a6-5ffc117af542
  user_id: ba14fea0-9d87-4f0c-b59e-32aaa8eb1434
  client_id: vmc

Demo of command line usage on cloudfoundry.com

The same command line example should work against a UAA running on cloudfoundry.com (except for the token decoding bit because you won't have the client secret). In this case, there is no need to run a local uaa server, so simply ask the external login endpoint to tell you about the system:

$ curl -H "Accept: application/json" uaa.cloudfoundry.com/login
{
  "prompts":{"username":["text","Username"],
    "password":["password","Password"]
  }
}

You can then try logging in with the UAA ruby gem. Make sure you have ruby 1.9, then

$ gem install cf-uaac
$ uaac target uaa.cloudfoundry.com
$ uaac token get [yourusername] [yourpassword]

(or leave out the username / password to be prompted).

This authenticates and obtains an access token from the server using the OAuth2 implicit grant, the same as used by a client like VMC.

Integration tests

With all apps deployed into a running server on port 8080 the tests will include integration tests (a check is done before each test that the app is running). You can deploy them in your IDE or using the command line with mvn tomcat:run and then run the tests as normal.

For individual modules, or for the whole project, you can also run integration tests and the server from the command line in one go with

$ mvn test -P integration

(This might require an initial mvn install from the parent directory to get the wars in your local repo first.)

To make the tests work in various environments you can modify the configuration of the server and the tests (e.g. the admin client) using a variety of mechanisms. The simplest is to provide additional Maven profiles on the command line, e.g.

$ (cd uaa; mvn test -P vcap)

will run the integration tests against a uaa server running in a local vcap, so for example the service URL is set to uaa.vcap.me (by default). There are several Maven profiles to play with, and they can be used to run the server, or the tests or both:

  • local: runs the server on the ROOT context http://localhost:8080/

  • vcap: also runs the server on the ROOT context and points the tests at uaa.vcap.me.

  • devuaa: points the tests at http://devuaa.cloudfoundry.com (an instance of UAA deployed on cloudfoundry).

All these profiles set the CLOUD_FOUNDRY_CONFIG_PATH to pick up a uaa.yml and (if appropriate) set the context root for running the server (see below for more detail on that).

BVTs

There is a really simple cucumber feature spec (--tag @uaa) to verify that the UAA server is there. There is also a rake task to launch the integration tests from the uaa submodule in vcap. Typical usage for a local (uaa.vcap.me) instance:

$ cd vcap/tests
$ rake bvt:run_uaa

You can change the most common important settings with environment variables (see below), or with a custom uaa.yml. N.B. MAVEN_OPTS cannot be used to set JVM system properties for the tests, but it can be used to set memory limits for the process etc.

Custom YAML Configuration

To modify the runtime parameters you can provide a uaa.yml, e.g.

$ cat > /tmp/uaa.yml
uaa:
  host: uaa.appcloud21.dev.mozycloud
  test:
    username: dev@cloudfoundry.org # defaults to vcap_tester@vmware.com
    password: changeme
    email: dev@cloudfoundry.org

then from vcap-tests

$ CLOUD_FOUNDRY_CONFIG_PATH=/tmp rake bvt:run_uaa

or from uaa/uaa

$ CLOUD_FOUNDRY_CONFIG_PATH=/tmp mvn test

The integration tests look for a Yaml file in the following locations (later entries override earlier ones), and the webapp does the same when it starts up so you can use the same config file for both:

classpath:uaa.yml
file:${CLOUD_FOUNDRY_CONFIG_PATH}/uaa.yml
file:${UAA_CONFIG_FILE}
${UAA_CONFIG_URL}

Using Maven with Cloud Foundry or VCAP

To test against a vcap instance use the Maven profile vcap (it switches off some of the tests that create random client and user accounts):

$ (cd uaa; mvn test -P vcap)

To change the target server it should suffice to set VCAP_BVT_TARGET (the tests prefix it with uaa. to form the server url), e.g.

$ VCAP_BVT_TARGET=appcloud21.dev.mozycloud mvn test -P vcap

You can also override some of the other most important default settings using environment variables. The defaults as usual come from uaa.yml but tests will search first in an environment variable:

  • UAA_ADMIN_CLIENT_ID the client id for bootstrapping client registrations needed for the rest of the tests.

  • UAA_ADMIN_CLIENT_SECRET the client secret for bootstrapping client registrations

All other settings from uaa.yml can be overridden individually as system properties. Running in an IDE this is easy just using whatever features allow you to modify the JVM in test runs, but using Maven you have to use the argLine property to get settings passed onto the test JVM, e.g.

$ mvn -DargLine=-Duaa.test.username=foo test

will create an account with userName=foo for testing (instead using the default setting from uaa.yml).

If you prefer environment variables to system properties you can use a custom uaa.yml with placeholders for your environment variables, e.g.

uaa:
  test:
    username: ${UAA_TEST_USERNAME:marissa}

will look for an environment variable (or system property) UAA_TEST_USERNAME before defaulting to marissa. This is the trick used to expose UAA_ADMIN_CLIENT_SECRET etc. in the standard configuration.

Using Maven with to test with postgresql or mysql

The default uaa unit tests (mvn test) use hsqldb.

To run the unit tests using postgresql:

$ SET SPRING_PROFILES_ACTIVE=test,postgresql 
& SET CLOUD_FOUNDRY_CONFIG_PATH=src/test/resources/test/profiles/postgresql 
& mvn test

To run the unit tests using mysql:

$ SET SPRING_PROFILES_ACTIVE=test,mysql 
$ SET CLOUD_FOUNDRY_CONFIG_PATH=src/test/resources/test/profiles/mysql 
& mvn test

The database configuration for the common and scim modules is located at:
common/src/test/resources/(mysql|postgresql).properties
scim/src/test/resources/(mysql|postgresql).properties

Inventory

There are actually several projects here, the main uaa server application and some samples:

  1. common is a module containing a JAR with all the business logic. It is used in the webapps below.

  2. uaa is the actual UAA server

  3. gem is a ruby gem (cf-uaa-client) for interacting with the UAA server

  4. api (sample) is an OAuth2 resource service which returns a mock list of deployed apps

  5. app (sample) is a user application that uses both of the above

  6. login (sample) is an application that performs authentication for the UAA acting as a back end service

In CloudFoundry terms

  • uaa provides an authentication service plus authorized delegation for back-end services and apps (by issuing OAuth2 access tokens).

  • api is a service that provides resources that other applications may wish to access on behalf of the resource owner (the end user).

  • app is a webapp that needs single sign on and access to the api service on behalf of users.

  • login is where Cloud Foundry administrators set up their authentication sources, e.g. LDAP/AD, SAML, OpenID (Google etc.) or social. The cloudfoundry.com platform uses a different implementation of the login server.

UAA Server

The authentication service is uaa. It's a plain Spring MVC webapp. Deploy as normal in Tomcat or your container of choice, or execute mvn tomcat:run to run it directly from uaa directory in the source tree (make sure the common jar is installed first using mvn install from the common subdirectory or from the top level directory). When running with maven it listens on port 8080.

The UAA Server supports the APIs defined in the UAA-APIs document. To summarise:

  1. The OAuth2 /authorize and /token endpoints

  2. A /login_info endpoint to allow querying for required login prompts

  3. A /check_token endpoint, to allow resource servers to obtain information about an access token submitted by an OAuth2 client.

  4. SCIM user provisioning endpoint

  5. OpenID connect endpoints to support authentication /userinfo and /check_id (todo). Implemented roughly enough to get it working (so /app authenticates here), but not to meet the spec.

Authentication can be performed by command line clients by submitting credentials directly to the /authorize endpoint (as described in UAA-API doc). There is an ImplicitAccessTokenProvider in Spring Security OAuth that can do the heavy lifting if your client is Java.

By default uaa will launch with a context root /uaa. There is a Maven profile local to launch with context root /, and another called vcap to launch at / with a postgresql backend.

Configuration

There is a uaa.yml in the application which provides defaults to the placeholders in the Spring XML. Wherever you see ${placeholder.name} in the XML there is an opportunity to override it either by providing a System property (-D to JVM) with the same name, or a custom uaa.yml (as described above).

All passwords and client secrets in the config files are plain text, but they will be inserted into the UAA database encrypted with BCrypt.

User Account Data

The default is to use an in-memory RDBMS user store that is pre-populated with a single test users: marissa has password koala.

To use Postgresql for user data, activate one of the Spring profiles hsqldb or postgresql.

The active profiles can be configured in uaa.yml using

spring_profiles: postgresql

or by passing the spring.profiles.active parameter to the JVM. For, example to run with an embedded HSQL database:

 mvn -Dspring.profiles.active=hsqldb tomcat:run

Or to use PostgreSQL instead of HSQL:

 mvn -Dspring.profiles.active=postgresql tomcat:run

To bootstrap a microcloud type environment you need an admin client. For this there is a database initializer component that inserts an admin client. If the default profile is active (i.e. not postgresql) there is also a vmc client so that the gem login works out of the box. You can override the default settings and add additional clients in uaa.yml:

oauth:
  clients:
    admin:    
      authorized-grant-types: client_credentials
      scope: read,write,password
      authorities: ROLE_CLIENT,ROLE_ADIN
      id: admin
      secret: adminclientsecret
      resource-ids: clients

The admin client can be used to create additional clients (but not to do anything much else). A client with read/write access to the scim resource will be needed to create user accounts. The integration tests take care of this automatically, inserting client and user accounts as necessary to make the tests work.

The API Application

An example resource server. It hosts a service which returns a list of mock applications under /apps.

Run it using mvn tomcat:run from the api directory (once all other tomcat processes have been shutdown). This will deploy the app to a Tomcat manager on port 8080.

The App Application

This is a user interface app (primarily aimed at browsers) that uses OpenId Connect for authentication (i.e. SSO) and OAuth2 for access grants. It authenticates with the Auth service, and then accesses resources in the API service. Run it with mvn tomcat:run from the app directory (once all other tomcat processes have been shutdown).

The application can operate in multiple different profiles according to the location (and presence) of the UAA server and the Login application. By default it will look for a UAA on localhost:8080/uaa, but you can change this by setting an environment variable (or System property) called UAA_PROFILE. In the application source code (src/main/resources) you will find multiple properties files pre-configured with different likely locations for those servers. They are all in the form application-<UAA_PROFILE>.properties and the naming convention adopted is that the UAA_PROFILE is local for the localhost deployment, vcap for a vcap.me deployment, staging for a staging deployment (inside VMware VPN), etc. The profile names are double barrelled (e.g. local-vcap when the login server is in a different location than the UAA server).

Use Cases

  1. See all apps

     GET /app/apps
    

    browser is redirected through a series of authentication and access grant steps (which could be slimmed down to implicit steps not requiring user at some point), and then the photos are shown.

  2. See the currently logged in user details, a bag of attributes grabbed from the open id provider

     GET /app
    

The Login Application

A user interface for authentication. The UAA can also authenticate user accounts, but only if it manages them itself, and it only provides a basic UI. The Login app can be branded and customized for non-native authentication and for more complicate UI flows, like user registration and password reset.

The login application is actually itself an OAuth2 endpoint provider, but delegates those features to the UAA server. Configuration for the login application therefore consists of locating the UAA through its OAuth2 endpoint URLs, and registering the login application itself as a client of the UAA. There is a login.yml for the UAA locations, e.g. for a local vcap instance:

uaa:
  url: http://uaa.vcap.me
  token:
    url: http://uaa.vcap.me/oauth/token
  login:
    url: http://uaa.vcap.me/login.do

and there is an environment variable (or Java System property), LOGIN_SECRET for the client secret that the app uses when it authenticates itself with the UAA. The Login app is registered by default in the UAA only if there are no active Spring profiles (so not at all in vcap). In the UAA you can find the registration in the oauth-clients.xml config file. Here's a summary:

id: login
secret: loginsecret
authorized-grant-types: client_credentials
authorities: ROLE_LOGIN
resource-ids: oauth

Use Cases

  1. Authenticate

     GET /login
    

    The sample app presents a form login interface for the backend UAA, and also an OpenID widget so the user can authenticate using Google etc. credentials.

  2. Approve OAuth2 token grant

     GET /oauth/authorize?client_id=app&response_type=code...
    

    Standard OAuth2 Authorization Endpoint. Client credentials and all other features are handled by the UAA in the back end, and the login application is used to render the UI (see access_confirmation.jsp).

  3. Obtain access token

     POST /oauth/token
    

    Standard OAuth2 Authorization Endpoint passed through to the UAA.

Login Server

Handles authentication on cloudfoundry.com and delegates all other identity management tasks to the UAA. Also provides OAuth2 endpoints issuing tokens to client apps for cloudfoundry.com (the tokens come from the UAA and no data are stored locally).

Running and Testing the Login Server

The Login Server is a standard JEE servlet application, and you can build a war file and deploy it to any container you like (mvn package and look in the target directory). For convenience there is also a Maven profile that will run the Login Server, the UAA and some sample apps all in the same container from the command line (assuming you have the UAA and Login Server cloned in separate directories with a common parent):

$ (cd uaa; mvn clean install)
$ cd login-server
$ mvn clean install
$ mvn tomcat:run -P integration

(Note that the tomcat7 plugin at the moment does not support running multiple apps in the same container - it's a bug that is fixed but not released as of September 2012.)

You can run the Login Server integration tests using the command line as well (as long as the UAA project is built and installed first as above):

$ mvn test -P integration

Login Server APIs

Overview

The Login Server:

  • is separate from the UAA but needs to be connected to one via HTTP(S)
  • provides SSO for web applications in the Cloud Foundry platform
  • manages and provides a branded HTML UI for authentication and OAuth approval
  • provides some other features via JSON APIs

Login Form: GET /login

Request: GET /login?error={error}
Response Body: form with all the relevant prompts
Response Codes: 200 - Success

Form Login: POST /login.do

Browser POSTs to /login.do with user credentials (same as UAA). Login Server returns a cookie that can be used to authenticate future requests (until the session expires or the user logs out).

Logout: GET /logout.do

The Login Server is a Single Sign On server for the Cloud Foundry platform (and possibly user apps as well), so if a user logs out he logs out of all the apps. Users need to be reminded of the consequences of their actions, so the recommendation for application authors is to

  • provide a local logout feature specific to the client application and use that to clear state in the client
  • on the success page for that logout provide a link to the Login Server logout endpoint with a message telling the user what will happen if he clicks it
  • provide a redirect in the link to the logout endpoint (see below) so that the user come back to a familiar place when logged out, otherwise he will just get the logged out success page from the Login Server

Request: GET /logout.do?redirect=http://myclient/loggedout
Request Headers: Cookie: JSESSIONID=8765FDUAYSFT7897
Response Codes:

200 - OK (if no redirect supplied)
302 - FOUND (if redirect supplied)

OAuth2 Endpoints

The standard authorize and token endpoints are available on the Login Server. They are passed through the request to the UAA, getting JSON responses from the UAA and re-rendering them if the user requested HTML.

Start Authorization: GET /oauth/authorize

Client applications usually send a redirect to User's browser with the request URI appropriate to the Client. Exactly the same as the UAA, but the response is rendered differently.

Request: example

GET /oauth/authorize?response_type=code&
  redirect_uri=https://myclient/callback&
  client_id=myclient&
  state=RANDOM

The request must be authenticated as a user, so usually a session cookie (JSESSIONID) is required, having been obtained previously through the Login page.

Obtain Authorization Code: POST /oauth/authorize

Exactly the same as the UAA. When user approves the browser sends user_oauth_approval=true (or false) and the Login Server sends back an authorization code (if one was previously requested).

Token Endpoint: POST /oauth/token

Obtain an access token, typically either with an authorization code or client credentials. Exactly the same as the UAA.

Login Info: GET /login

Reports basic information about the build (version and git commit id) and also passes through the information about prompts from the UAA. Unauthenticated.

Healthz: GET /healthz

Returns "ok" in the response body if the server is up and running

Varz: GET /varz

Reports basic management information about the Login Server and the JVM it runs in (memory usage etc.). Secured with HTTP Basic authentication using credentials that are advertised on NATS in Cloud Foundry (for a standalone instance the default is varz:varzclientsecret).

Request: GET /varz
Response Body:

{
  "type": "Login",
  "links": {
    "JMImplementation": "http://localhost:8080/uaa/varz/JMImplementation",
    "spring.application": "http://localhost:8080/uaa/varz/spring.application",
    "com.sun.management": "http://localhost:8080/uaa/varz/com.sun.management",
    "Catalina": "http://localhost:8080/uaa/varz/Catalina",
    "env": "http://localhost:8080/uaa/varz/env",
    "java.lang": "http://localhost:8080/uaa/varz/java.lang",
    "java.util.logging": "http://localhost:8080/uaa/varz/java.util.logging"
  },
  "mem": 19173496,
  "memory": {
    "verbose": false,
    "non_heap_memory_usage": {
      "max": 184549376,
      "committed": 30834688,
      "init": 19136512,
      "used": 30577744
    },
    "object_pending_finalization_count": 0,
    "heap_memory_usage": {
      "max": 902299648,
      "committed": 84475904,
      "init": 63338496,
      "used": 19173496
    }
  },
  "spring.profiles.active": []
}

Autologin

For user-facing account management UIs (e.g. portal) that need to set or reset users' passwords it is convenient to be able to log them in immediately, rather than waiting for them to come back to the Login Server and enter the new password explicitly.

  1. Client POSTs user credentials to /autologin

  2. Login Server responds with autologin code (short-lived, one-time)

  3. Client builds redirect URI to Login Server /authorize endpoint (normal OAuth2 auth code flow) appending the code

  4. Client sends redirect to User

  5. User's browser initiates auth code flow

  6. Login Server redeems autologin code and exchanges it for an authenticated user (as if the user had authenticated with the Login Server manually)

  7. User's browser now has SSO cookie for Login Server, and remains logged in for the duration of that session.

Obtain Autologin Code: POST /autologin

Gets a short-lived code that can be exchanged for an authentication at the Login Server /oauth/authorize UI. The client authenticates itself with its secret using an HTTP Basic header.

Request: POST /autologin
Request Body: Form encoded user credentials

username=<username>&password=<password>

Request Headers:

Authorization: Basic <...>

Response Body:

{ "code"="aiuynklj", "path"="/oauth/authorize" }

By default the password is required and is checked using the login.do endpoint at the UAA, but could be made optional or changed to other authentication credentials with a configuration change in the Login Server (by adding a different AuthenticationManager to the AutologinController).

UAA Sysadmin Guide

UAA

UAA is the User Account and Authentication service. It is used to coordinate identity operations.

A “client” in the context of identity is an application doing something on behalf of the end user (or on behalf of itself).

It’s important to know how data flows between client applications and the UAA. The sequences of data movement satisfy different identity requirements.

The login flow: (view diagram source)

The client keeps track of the browser through a cookie to track its http(s) session. The refresh and access tokens are kept private and not shared directly with the browser.

An authenticated operation flow: (view diagram source)

This flow takes place after the authentication flow above. The browser can now make a request to the portal. The portal looks up the appropriate token from the session and uses it to make the request.

Login server

The login server component is separate from UAA so it can present an appropriate visual rendering of the login page and authentication interfaces.

The login server also has additional logic to support the autologin flow. This is to allow a client to sign in on behalf of the user using the client’s own credentials. This is needed when a user needs to be signed in after resetting his password.

The autologin flow: (view diagram source)

Local development and deployment

These apply if you are developing identity integration in your own application, outside a bosh deployment scenario.

Requirements:

maven 3.0.4
java >= 1.6

Older versions of maven will likely appear to work at first but eventually fail with an unhelpful error. Be sure mvn -v reports 3.0.4. It’s best if you only have one version installed.

Clone, build UAA server:

git clone git@github.com:cloudfoundry/uaa.git
cd uaa
mvn clean install

Note the version number that you just built (e.g. look in the pom or in uaa/target for the version label on the WAR file).

Clone, build login-server:

git clone git@github.com:cloudfoundry/login-server.git
cd login-server
mvn clean install

Run Servers (using the UAA version <X> from above):

cd login-server && mvn tomcat:run -P integration 

You can add -Didentity.version=<X> if you need to match a specific UAA build.

or to just run the UAA:

cd uaa && mvn tomcat7:run)

Configuration

uaa.yml drives uaa behavior. There is a default file in the WAR that you should not touch. Overrides and additions can come from an external location, the most convenient way to specify that is through an environment variable (or system property in the JVM): $CLOUDFOUNDRY_CONFIG_PATH/uaa.yml.

Database

UAA will use an in-memory database that is torn down between runs unless you choose a spring profile or a specific database configuration as a toplevel setting in uaa.yml. An example connecting to a postgres database:

database:
   driverClassName: org.postgresql.Driver
   url: jdbc:postgresql://localhost:5432/uaadb
   username: postgres
   password: password

Token signing

UAA can use either symmetric key encryption (shared secrets) or public key encryption.

jwt:
   token:
      signing-key: 
      verification-key: 

If you want to use symmetric key encryption, signing and verification values should be the same.

Generating new asymmetric key pairs

mkdir temp_uaa_certs
cd temp_uaa_certs
openssl genrsa -out privkey.pem 2048
openssl rsa -pubout -in privkey.pem -out pubkey.pem

Aysmmetric key pairs can be set directly in the yaml file using block literals. Make sure the entire key is indented.

jwt:
   token:
      signing-key: |
         -----BEGIN RSA PRIVATE KEY-----
         MIIEowIBAAKCAQEAyV3Ws3gLOvi169ZPx8v3t9UZpxcG0fqtQzC4X+Ff7dlx4w6P
         ...
         pYPYK4M+4Gwi7O49a63G+lzX7BqUWYBXR84iZG+vWz2F3ICjiOIz
         -----END RSA PRIVATE KEY-----
      verification-key: |
         -----BEGIN PUBLIC KEY-----
         MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyV3Ws3gLOvi169ZPx8v3
         ...
         XwIDAQAB
         -----END PUBLIC KEY-----

Clients

Specify autoapprove in the client section when the user should not be asked to approve a token grant expicitly. This avoids redundant and annoying requests to grant permission when there is not a reasonable need to ever deny them.

oauth:
   client:
      autoapprove:
         - vmc
         - support-signon

Individual client settings in uaa.yml go in sections under “clients” using the client name:

oauth:
   clients:
      portal:
         override: true
         scope: openid,cloud_controller.read,cloud_controller.write
         authorities: openid,cloud_controller.read,cloud_controller.write

Override defaults to false; when true, the client settings in this section can override client settings saved if you have a persistent database. It’s recommended to have this property present and set to true; declare it as false only if you need the db to take precedence.

Access Control Data

A scope specifies a privilege users can ask this client to assert on their behalf.

An authority specifies a privilege the client can assert on its own.

User Bootstrapping

uaa.yml entries can used to set up users for development. This is not suitable for staging or production but useful in testing. If you specified a persistent db above and the user account exists, it may not be updated with a new password. Group membership will be updated automatically in a future release.

scim is a toplevel attribute in uaa.yml. Login, password, and groups can be defined on the new user.

scim:
   users:
      - sre@vmware.com|apassword|scim.write,scim.read,openid

A scope cannot be added to a token granted by the UAA unless the user is in the corresponding group with the same name (some default groups are always available and do not need to be explicitly populated: openid, password.write, cloud_controller.read, cloud_controller.write, tokens.read, tokens.write).

Bosh development & debug

Bosh deployments can be tricky to debug.

You should examine the steps of the flow you are expecting and find the point at which it misbehaves. If any one point in the flow is broken, for example an endpoint misconfigured or an identity test failing, you will see the flow break down at that point.

vms to look at are uaa, login, and the vm with your client application.

Go the uaa machine to monitor logs with:

bosh ssh uaa 0
tail -f /var/vcap/sys/log/uaa/uaa.log

You can watch headers to confirm the kind of flow you want with tcpdump, for example if you ssh into the login server:

bosh ssh login 0
sudo tcpdump 'tcp port 80 and host uaa.cf116.dev.las01.vcsops.com' -i any -A

uaac and vmc can take a --trace option which shows each online interaction.

"uaa target" your uaa if you haven't already.

"uaac token decode" functions can be used to examine tokens. Make sure attributes like scopes match what you expect. This function can take a verification key to make sure the token is signed as you expect.

"uaac signing key" can be used to get the signing key the uaa server is using. Pass -c and -s for a client to retrieve a symmetric key.

Live data viewing and manipulation

vmc and uaac each need a target. vmc points to a cloud controller and uaac to a uaa instance.

vmc target api.cf116.dev.las01.vcsops.com
uaac target uaa.cf116.dev.las01.vcsops.com # dev deployment
uaac target uaa.cfpartners.cloudfoundry.com # production
uaac target localhost:8080/uaa # local dev

uaac context will contain clients or an end user id. These are added to your context after authenticating.

uaac token client get admin # default pass adminsecret
uaac token client get vmc
uaac token client get dashboard # get dashboard context

Learn about your context

uaac contexts # show your target and all contexts with it

You see scopes granted through this token. jti is a token identifier, used for operations like deleting a token.

Access to Users and Groups

User, group, and client changes below will be persisted if you have UAA backed by a persistent db.

If your admin client is denied access to modify scim, you will need to add scim.write to its authorities list, delete and get the token again.

uaac client update admin --authorities "clients.write clients.read uaa.admin scim.read scim.write"
uaac token delete
uaac token client get admin

Manage Users

The vmc client can be used for user registrations:

vmc add-user --email sre@vmware.com # prompts for new password
uaac users # examine all users
uaac user ids # look up user ids -- only works outside production

Register a new user

uaac user add

Manage Groups

Groups limit what scopes an entity has and what can be delegated by this client or user.

Make a user a member of the dashboard group to open the dashboard:

uaac member add dashboard.user sre@vmware.com
uaac -t user add --given_name Bill --emails bt@vmware.com --password test bt@vmware.com

Manage client registrations

Clients registrations can also be changed in a live system.

uaac token client get admin # admin has client scopes
uaac clients # list the clients uaa knows about

Create new clients:

uaac client add media_server --scope openid,scim.read,scim.write \
                             --authorized_grant_types client_credentials \
                             --authorities oauth.login

Run vcap yeti tests with a deployment

Put in .bash_profile or another script you source:

export VCAP_BVT_TARGET=api.cf116.dev.las01.vcsops.com
export VCAP_BVT_USER=sre@vmware.com
export VCAP_BVT_USER_PASSWD=an_admin_pw

Make sre@vmware.com an admin if you want to do parallel yeti tests

uaac user update sre@vmware.com --authorities "cloud_controller.admin"

Manually deploy an app

vmc login
vmc create-org org1
vmc login
vmc create-space space1
vmc login # select space1
vmc push # in an app dir

Execute the yeti suite with retries in case of timeouts

vmc target api.cf116.dev.las01.vcsops.com
vmc login # sre@vmware.com
vmc add-user --email admin@vmware.com
git clone https://github.com/cloudfoundry/vcap-yeti.git
cd vcap-yeti
git checkout
./update
bundle exec rake full rerun_failure # admin@vmware.com test

UAA Signing

Tokens are signed by the UAA. Signatures are checked for validity. Get the configuration of the UAA signing key if you are dealing with invalid token errors.

This will print the public key without requiring a password if using public key verification:

uaac signing key

if access is denied, use client credentials that allow access to the symmetric key:

vmc signing key -c admin -s adminsecret

Additional Resources

UAA documentation in docs/

  1. UAA-APIs.rst: API document, kept updated
  2. UAA-CC-ACM-VMC-Interactions.rst: flows for operations between parts
  3. UAA-Overview.rst: comparisons with oauth2
  4. UAA-Security.md: accounts, bootstrapping, scopes for access control
  5. UAA_presentation.pdf: Overview presentation, outline for internal developers
  6. CF-Identity-Services-Preface.rst: justification and design overview

Login-server documentation in docs/

  1. Login-APIs.md: login-server specifics like autologin

UAA Features and Configuration

It is the responsibility of a Resource Server to extract information about the user and client application from the access token and make an access decision based on that information. This guide will help authors of resource Servers and maintainers of client and user account data to understand the range of information available and the kinds of decisions that can be taken. The UAA itself is a Resource Server, so the access decisions taken by the UAA are used as an example.

User Accounts

Security Metadata

User accounts are either of type "user" or type "admin" (using the SCIM type field from the core schema). These translate into granted authorities, [uaa.user] or [uaa.admin,uaa.user] respectively, for the purposes of access decisions within the UAA (i.e. admin users also have the user role). Granted authorities are not directly visible to Resource Servers, but they show up as scopes in the access tokens.

Resource Servers may choose to use this information as part of an access decision, and this may be good enough for simple use cases (e.g. users belong to a small number of relatively static roles), but in general they will need to maintain their own acess decision data since roles on UAA don't necessarily correspond to the same thing on a Resource Server.

Support for SCIM groups is currently provided only through the authorities attribute of the user object. Resource Servers that are also SCIM clients can modify this attribute themselves, but it might be better (and safer) if the data don't change much to have an admin user or client do the role assignments. In any case it is recommended that Resource Servers have sensible defaults for new users that have not yet been assigned a role.

Bootstrap

There are 2 distinct scenarios:

  1. Demo or test with vanilla code and no special environment. A UAA service started with no active Spring profile will initialize a single user account (marissa/koala).

  2. A vcap environment: integration testing or in production. If the service starts with any active Spring profile by default it will not touch the user database. The SCIM endpoints can be used to provision user accounts, once a client with the correct privileges has been registered.

In either case additional user accounts and client registrations can be bootstrapped at start up by providing some data in uaa.yml. Example users:

scim:
  users:
    - paul|wombat|paul@test.org|Paul|Smith|uaa.admin
    - stefan|wallaby|stefan@test.org|Stefan|Schmidt

The format for the user is username|password|email|first_name|last_name(|comma-separated-authorities). Remember that authorities are represented as groups in SCIM.

Account lockout policy

In its default configuration, the UAA does not lock accounts permanently when a user repeatedly fails authentication. Instead it temporarily locks a user out for a short period (5 minutes by default) after 5 failed logins within the previous hour. The failure count is reset when a user successfully authenticates.

OAuth Client Applications

Security Metadata

Client application meta data can be used by Resource Servers to make an access decision, and by the Authorization Server (the UAA itself) to decide whether to grant an access token.

Scope values are arbitrary strings, but are a contract between a client and a Resource Server, so in cases where UAA acts as a Resource Server there are some "standard" values (scim.read, scim.write, passsword.write, openid, etc.) whose usage and meaning is described below. Scopes are used by the Authorization Server to deny a token requested for a scope not on the list, and should be used by a Resource Server to deny access to a resource if the token has insufficient scope.

UAA client applications have the following meta data (some are optional, but to prevent mistakes it is usually better to use a default value):

  • authorized-grant-types: a comma-separated list of OAuth2 grant types, as defined in the spec: choose from client_credentials, password, implicit, refresh_token, authorization_code. Used by the Authorization Server to deny a token grant if it is not on the list. If in doubt use authorization_code and refresh_token.
  • scope: a list of permitted scopes for this client to obtain on behalf of a user (so not relevant to client_credentials grants). Also used as the default scopes for a token where the client does not explicitly specify scopes in the authorization request.
  • authorities: a list of granted authorities for the client (e.g. uaa.admin or any valid scope value). The authorities are used to define the default scopes that are assigned to a token in a client_credentials grant, and to limit the legal values if explicit scopes are requested in that case.
  • secret: the shared secret used to authenticate token grant requests and token decoding operations (not revealed to Resource Server).
  • resource-ids: white list of resource ids to be included in the decoded tokens granted to this client. The UAA does not store any data here (it should be none for all clients), but instead creates a list of resource ids dynamically from the scope values when a token is granted. The resource id is extracted from a scope using a period separator (the last occurrence in the string) except for some standard values (e.g. openid) that are not controlled by the UAA or its own resources. So a scope of cloud_controller.read is assigned a resource id of cloud_controller, for instance.

Bootstrap

Client registration can be initialized by adding client details data to uua.yml. The UAA always starts with a registered admin client. There are 2 typical scenarios for additional client registration bootstraps:

  1. Demo or test with vanilla code and no custom uaa.yml. A UAA service started with no active Spring profile will start with some client registrations (used in samples to make the out-of-the box experience for new users as convenient as possible). More clients and user accounts will be created by the integration tests.

  2. A vcap environment: integration testing or in production. By default no clients are created if any Spring profile is active, but client registrations can be configured in uaa.yml and in some well-known situations clients this will happen. In particular, the dev_setup environment and the CF.com deployment job both start up with additional client registrations that are needed by the basic Cloud Foundry use cases (vmc and cloud_controller). If the vcap Spring profile is active in the integration tests, no additional accounts will be created.

Clients are bootstrapped from config if they are not present in the backend when the system starts up (i.e. once the system has started up once config changes will not affect the client registrations for existing clients). Certain fields (e.g. secret) can be reset if the bootstrap component is configured to do so (it is not by default).

The admin client has the following properties (in the default uaa.yml always present on the classpath but overriddable by specifying all the values again in a custom config file):

  id: admin
  secret: adminsecret
  authorized-grant-types: client_credentials
  scope: none
  authorities: uaa.admin,clients.read,clients.write,clients.secret

The admin client can be used to bootstrap the system by adding additional clients. In particular, user accounts cannot be provisioned until a client with access to the scim resource is added.

Demo Environment

The default Spring profile initializes 3 clients in addition to the admin client, e.g. if the server is started from the command line after a fresh clone from github for demo purposes:

vmc:
  id: vmc
  authorized-grant-types: implicit
  scope: cloud_controller.read,cloud_controller.write,openid,password.write
  authorities: uaa.none
  resource-ids: none
app:
  id: app
  secret: appclientsecret
  authorized-grant-types: password,authorization_code,refresh_token
  scope: cloud_controller.read,cloud_controller.write,openid,password.write,tokens.read,tokens.write
  authorities: uaa.none
  resource-ids: none

VCAP Dev Setup

In dev_setup these client accounts (in addition to the admin client) are initialized:

cloud_controller:
  authorized-grant-types: client_credentials
  scope: none
  authorities: scim.read,scim.write,password.write,tokens.read,tokens.write
  id: cloud_controller
  secret: ...
  resource-ids: none
vmc:
  id: vmc
  authorized-grant-types: implicit
  scope: cloud_controller.read,cloud_controller.write,openid,password.write
  authorities: uaa.none
  resource-ids: none
  redirect-uri: http://uaa.cloudfoundry.com/redirect/vmc

The cloud controller secret is generated during the setup. The same clients are initialized in CF.com, but the secret is different. Additional clients can be added during start up using uaa.yml, e.g.

oauth:
  clients:
    vmc:
      authorized-grant-types: implicit
      scope: cloud_controller.read,cloud_controller.write,password.write,openid
      authorities: uaa.none
      id: vmc
      resource-ids: none
      redirect-uri: http://uaa.cloudfoundry.com/redirect/vmc

Token Scope Rules

When a client application asks for a new access token it can optionally provide a set of requested scopes (space separated, e.g. scope=openid cloud_controller.read). The UAA will use that set if provided and that will be the scope of the token if granted. Otherwise, if no explicit value is requested, defaults will be supplied according to what the client and user are allowed to do. The rules governing the defaults and what is allowed are described next.

User Tokens

A token granted on behalf of a user (grant type anything except client_credentials) takes its default scopes from the scope field of the client registration. Whether or not the default values are used, the requested scopes are then validated:

  • The user's authorities (SCIM groups) are augmented with some static values, configurable but defaulting to [openid, cloud_controller.read, cloud_controller.write]
  • Allowed scopes consist of the intersection of the client scope and the augmented user authorities.
  • Disallowed scopes are removed from the request.
  • If all the requested scopes are disallowed then clients get a 400 response with a JSON error message indicating the allowed values (for implicit grants it should be a 302 according to the OAuth2 spec, but that change hasn't been implemented yet). The exception to that rule is for clients with no registered scopes (no error in that case), but there shouldn't be any such clients in a production system.

Note that the filtering of scopes by user authorities might mean that a client gets a narrower-scoped token than it originally asked for, e.g. if it asks for no scope=dash.admin dash.user openid, the token might come back with only dash.user openid. Tokens are opaque to client applications, so they have to be prepared for resource servers to deny access to some resources based on the scope of the token when it is presented.

Client Tokens

A token issued on a client_credentials grant has default and allowed scopes equal to the client authorities. Requesting a disallowed scope will result in a 400 reponse and an error message that indicates the allowed scopes. A client would normally take the default scopes when acting on its own behalf - since no approval is necessary there is no point narrowing the scope.

UAA Resources

All OAuth2 protected resource have an id (as listed individually). Any request whose token does not have a matching resource id (aud field in decoded token) will be rejected. Resources that are not OAuth2 protected resources do not have a resource id (e.g. those with simple HTTP Basic authentication).

Token Management

Resource ID = tokens. Rules:

  • Revoke user token:
    • Token has scope uaa.admin, or
    • If token represents user, user is authenticated and is the owner of the token to be revoked, and token has scope tokens.write
  • List user tokens:
    • Token has scope uaa.admin or
    • If token represents user, user is authenticated and is the owner of the token to be read, and token has scope tokens.read
  • Revoke client token:
    • Token has scope uaa.admin or
    • Token represents the client in the token to be revoked, and token has scope tokens.write
  • List client tokens:
    • Token has scope uaa.admin or
    • Token represents the client in the token to be revoked, and token has scope tokens.read

Client Registration

Resource ID = clients. Rules:

  • Remove, update or add client registration
    • Token has scope clients.write
  • Inspect client registration
    • Token has scope clients.read

Client Secret Mangagement

Resource ID null (so all clients can change their password). Rule:

  • Change secret
    • Token has scope clients.secret
    • Either token has scope uaa.admin or client can only change its own secret
    • Either token has scope uaa.admin or client provides the old secret
    • Even if token has scope uaa.admin client must provide the old value to change its own secret

Password Change

Resource ID = password. Rules:

  • Change password
    • Token has scope password.write
    • If token represents a client, scope includes uaa.admin
    • If token represents a user, either scope includes uaa.admin or user provides the old password

User Account Management

Resource ID = scim. Rules:

  • List or search users

    • Token with scope scim.read provides read/query access to ALL users in the UAA
  • Delete, add user account

    • Token has scope scim.write
  • Update existing user account

    • Token with scope scim.write lets you update ANY user's information in the UAA

In addition, a User Token obtained by a client with authorities scim.me (eg. token from authorization_code or password grant flow) provides read/query/update access to that particular user's account.

Username from ID Queries

Resource ID = scim. Rules:

  • Obtain username information via /ids/Users
  • filter parameter must be supplied
  • Only attributes userName and id are returned (and can be queried on)

User Profiles

Used for Single Sign On (OpenID Connect lite). Resource ID = openid. Rules:

  • Obtain user profile data
    • Token has scope openid

Groups & Membership Management

Resource ID = scim. Rules:

  • List or Search groups

    • Token has scope scim.read
  • Delete or Add groups

    • Token has scope scim.write
  • Update group name or add/remove members

    • Token has either scim.write OR groups.update

In addition, a User Token obtained by a client with authorities scim.me (eg. token from authorization_code or password grant flow) provides the following access:

  • List or Search groups

    • Response contains the group(s) that lists the user as a reader.
  • Update group name or add/remove members

    • The user is listed as a writer in the group being updated.

Token Resources for Providers

The UAA uses HTTP Basic authentication for these resources, so they are no OAuth2 protected resources, but to simplify the security data client registrations are used, so only registered clients can access them. The caller must have a secret (so vmc and other implicit grant clients need not apply).

  • Obtain access token at /oauth/token

    • Client is authenticated
    • If grant type is authorization_code client must have the code
  • Inspect access token at /check_token

    • Client is authenticated
    • Client has authority uaa.resource
  • Obtain token key (for decoding JWT tokens locally) at /token_key

    • Client is authenticated
    • Client has authority uaa.resource

Management Information

The /varz endpoint is protected by HTTP Basic authentication with credentials that are externalized via uaa.yml. They have defaults (varz:varzclientsecret) and can also be overridden via System properties.

Login Prompts

The login endpoint is unsecured. Any client can ask it and it will respond with some information about the system and the login prompts required to authenticate.

User Account and Authentication Service APIs

Table of Contents

Overview

The User Account and Authentication Service (UAA):

  • is a separate application from the Cloud Controller
  • owns the user accounts and authentication sources
  • is called via JSON APIs
  • supports standard protocols to provide single sign-on and delegated authorization to web applications in addition to JSON APIs to support the Cloud Controller and team features of Cloud Foundry
  • supports APIs and a basic login/approval UI for web client apps
  • supports APIs for user account management for an external web UI (i.e. www.cloudfoundry.com)

Rather than trigger arguments about how RESTful these APIs are we'll just refer to them as JSON APIs. Most of them are defined by the specs for the OAuth2, OpenID Connect, and SCIM standards.

Configuration Options

Several modes of operation and other optional features can be set in configuration files. Settings for a handful of standard scenarios can be externalized and switched using environment variables or system properties.

  • Internal username/password authentication source

    The UAA manages a user account database. These accounts can be used for password based authentication similar to existing Cloud Foundry user accounts. The UAA accounts can be configured with password policy such as length, accepted/required character types, expiration times, reset policy, etc.

  • Other Authentication sources

    Other standard external authentication sources can also be used. The most common and therefore the expected starting point are LDAP server, or an external OpenID provider (e.g. Google). Another expected authentication source would be Horizon Application Manager either through OAuth2 (preferred), or SAML protocols. General SAML2 support is not currently planned but could be added and would provide capabilities similar to OpenID and OAuth.

Authentication and Delegated Authorization APIs

This section deals with machine interactions, not with browsers, although some of them may have browsable content for authenticated users. All machine requests have accept headers indicating JSON (or a derived media type perhaps).

The /userinfo, /check_id, and /token endpoints are specified in the OpenID Connect and OAuth2 standards and should be used by web applications on a cloud foundry instance such as micro, www, support, but will not be used by flows from vmc.

A Note on OAuth Scope

The OAuth2 spec includes a scope parameter as part of the token granting request which contains a set of scope values. The spec leaves the business content of the scope up to the participants in the protocol - i.e. the scope values are completely arbitrary and can in principle be chosen by any Resource Server using the tokens. Clients of the Resource Server have to ask for a valid scope to get a token, but the Authorization Server itself attaches no meaning to the scope - it just passes the value through to the Resource Server. The UAA implementation of the Authorization Server has a couple of extra scope-related features (by virtue of being implemented in Spring Security where the features originate).

  1. There is an optional step in client registration, where a client declares which scopes it will ask for, or alternatively where the Authorization Server can limit the scopes it can ask for. The Authorization Server can then check that token requests contain a valid scope (i.e. one of the set provided on registration).
  2. The Resource Servers can each have a unique ID (e.g. a URI). And another optional part of a client registration is to provide a set of allowed resource ids for the client in question. The Authorization Server binds the allowed resource ids to the token and then provides the information via the /check_token endpoint (in the aud claim), so that a Resource Server can check that its own ID is on the allowed list for the token before serving a resource.

Resource IDs have some of the character of a scope, except that the clients themselves don't need to know about them - it is information exchanged between the Authorization and Resource Servers. The examples in this document use a scope parameter that indicates a resource server, e.g. a Cloud Controller instance. This is a suggested usage, but whether it is adopted by the real Cloud Controller is not crucial to the system. Similarly any Resource Server that wants to can check the allowed resource IDs if there are any, but it is not mandatory to do so.

Authorization Code Grant

This is a completely vanilla as per the OAuth2 spec, but we give a brief outline here for information purposes.

Browser Requests Code: GET /oauth/authorize

HTML Responses

  • Request: GET /oauth/authorize
  • Request Body: some parameters specified by the spec, appended to the query component using the application/x-www-form-urlencoded format,
    • response_type=code
    • client_id=www
    • scope=read write password
    • redirect_uri is optional because it can be pre-registered
  • Request Header:
    • Cookie: JSESSIONID=ADHGFKHDSJGFGF; Path / - the authentication cookie for the client with UAA. If there is no cookie user's browser is redirected to /login, and will eventually come back to /oauth/authorize.
  • Response Header: location as defined in the spec includes access_token if successful:

    HTTP/1.1 302 Found
    Location: https://www.cloudfoundry.example.com?code=F45jH
  • Response Codes:

    302 - Found

Non-Browser Requests Code: GET /oauth/authorize

JSON Responses

If the client asks for a JSON response (with an Accept header), and the user has not approved the grant yet, the UAA sends a JSON object with some useful information that can be rendered for a user to read and explicitly approve the grant:

{
  "message":"To confirm or deny access POST to the following locations with the parameters requested.",
  "scopes":[
    {"text":"Access your data with scope 'openid'","code":"scope.openid"},
    {"text":"Access your 'cloud_controller' resources with scope 'read'","code":"scope.cloud_controller.read"},
    ...],
  ...,
  "client_id":"idtestapp",
  "redirect_uri":"http://nowhere.com",
  "options":{
    "deny":{"location":"https://uaa.cloudfoundry.com/oauth/authorize","value":"false","path":"/oauth/authorize","key":"user_oauth_approval"},
    "confirm":{"location":"https://uaa.cloudfoundry.com/oauth/authorize","value":"true","path":"/oauth/authorize","key":"user_oauth_approval"}
  }
}

The most useful information for constructing a user approval page is the list of requested scopes, the client id and the requested redirect URI.

Client Obtains Token: POST /oauth/token

See oauth2 token endpoint below for a more detailed description.

Request POST /oauth/token

Request Body

the authorization code (form encoded), e.g.:

code=F45jH
Response Codes 200 OK

Response Body

{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"bearer",
"expires_in":3600,
}

Implicit Grant with Credentials: POST /oauth/authorize

An OAuth2 defined endpoint to provide various tokens and authorization codes.

For the vmc flows, we use the OAuth2 Implicit grant type (to avoid a second round trip to /token and so vmc does not need to securely store a client secret or user refresh tokens). The authentication method for the user is undefined by OAuth2 but a POST to this endpoint is acceptable, although a GET must also be supported (see OAuth2 section 3.1).

Effectively this means that the endpoint is used to authenticate and obtain an access token in the same request. Note the correspondence with the UI endpoints (this is similar to the /login endpoint with a different representation).

Note

A GET mothod is used in the relevant section of the spec that talks about the implicit grant, but a POST is explicitly allowed in the section on the /oauth/authorize endpoint (see OAuth2 section 3.1).

All requests to this endpoint MUST be over SSL.

  • Request: POST /oauth/authorize
  • Request query component: some parameters specified by the spec, appended to the query component using the "application/x-www-form-urlencoded" format,
    • response_type=token
    • client_id=vmc
    • scope=read write
    • redirect_uri - optional because it can be pre-registered, but a dummy is still needed where vmc is concerned (it doesn't redirect) and must be pre-registered, see Client Registration Administration APIs.
  • Request body: contains the required information in JSON as returned from the login information API, e.g. username/password for internal authentication, or for LDAP, and others as needed for other authentication types. For example:

    credentials={"username":"dale","password":"secret"}
  • Response Header: location as defined in the spec includes access_token if successful:

    HTTP/1.1 302 Found
    Location: oauth:redirecturi#access_token=2YotnFZFEjr1zCsicMWpAA&token_type=bearer
  • Response Codes:

    302 - Found

Implicit Grant for Browsers: GET /oauth/authorize

This works similarly to the previous section, but does not require the credentials to be POSTed as is needed for browser flows.

  1. The browser redirects to the /oauth/authorize endpoint with parameters in the query component as per the previous section.
  2. The UAA presents the UI to authenticate the user and approve the scopes.
  3. If the user authorizes the scopes for the requesting client, the UAA will redirect the browser to the redirect_uri provided (and pre-registered) by the client.
  4. Since the reply parameters are encoded in the location fragment, the client application must get the access token in the reply fragment from user's browser -- typically by returning a page to the browser with some javascript which will post the access token to the client app.

Trusted Authentication from Login Server

In addition to the normal authentication of the /oauth/authorize endpoint described above (cookie-based for browser app and special case for vmc) the UAA offers a special channel whereby a trusted client app can authenticate itself and then use the /oauth/authorize endpoint by providing minimal information about the user account (but not the password). This channel is provided so that authentication can be abstracted into a separate "Login" server. The default client id for the trusted app is login, and this client is registered in the default profile (but not in any other):

id: login,
secret: loginsecret,
scope: uaa.none,
authorized_grant_types: client_credentials,
authorities: oauth.login

To authenticate the /oauth/authorize endpoint using this channel the Login Server has to provide a standard OAuth2 bearer token header _and some additional parameters to identify the user: source=login is mandatory, as is username, plus optionally [email, given_name, family_name]. The UAA will lookup the user in its internal database and if it is found the request is authenticated. The UAA can be configured to automatically register authenicated users that are missing from its database, but this will only work if all the fields are provided. The response from the UAA (if the Login Server asks for JSON content) has enough information to get approval from the user and pass the response back to the UAA.

Using this trusted channel a Login Server can obtain authorization (or tokens directly in the implicit grant) from the UAA, and also have complete control over authentication of the user, and the UI for logging in and approving token grants.

An authorization code grant has two steps (as normal), but instead of a UI response the UAA sends JSON:

Step 1: Initial Authorization Request

  • Request: POST /oauth/authorize
  • Request query component: some parameters specified by the spec, appended to the query component using the "application/x-www-form-urlencoded" format,
    • response_type=code
    • client_id - a registered client id
    • redirect_uri - a redirect URI registered with the client
    • state - recommended (a random string that the client app can correlate with the current user session)
    • source=login - mandatory
    • username - the user whom the client is acting on behalf of (the authenticated user in the Login Server)
    • email - the email of the user, optional
    • given_name - the given (first) name of the user, optional
    • family_name - the family (last) name of the user, optional
  • Request header:

    Accept: application/json Authorization: Bearer <login-client-bearer-token-obtained-from-uaa>

  • Request body: empty (or form encoded parameters as above)
  • Response header will include a cookie. This needs to be sent back in the second step (if required) so that the UAA can retrive the state from this request.
  • Response body if successful, and user approval is required (example):

    HTTP/1.1 200 OK
    {
      "message":"To confirm or deny access POST to the following locations with the parames rqstd.",
      "scopes":[
         {"text":"Access your data with scope 'openid'","code":"scope.openid"},
         {"text":"Access your 'password' resources with scope 'write'","code":"scope.password.write"},
         ...
      ],
      "auth_request":{...}, // The authorization request
      "client": {
         "scope":[...],
         "client_id":"app",
         "authorized_grant_types":["authorization_code"],
         "authorities":[...]
      },
      "redirect_uri": "http://app.cloudfoundry.com",
      "options":{
          "deny":{"value":"false","key":"user_oauth_approval",...},
          "confirm":{"value":"true","key":"user_oauth_approval",...}
      }
    }

    the response body contains useful information for rendering to a user for approval, e.g. each scope that was requested (prepended with "scope." to facilitate i18n lookups) including a default message text in English describing it.

  • Response Codes:

    200 - OK
    403 - FORBIDDEN (if the user has denied approval)
    302 - FOUND (if the grant is already approved)

Step 2: User Approves Grant

Just a normal POST with approval parameters to /oauth/authorize, including the cookie requested in Step 1 (just like a browser would do). For example:

POST /oauth/authorize
Cookie: JSESSIONID=fkserygfkseyrgfv

user_oauth_approval=true

Response:

302 FOUND
Location: https://app.cloudfoundry.com?code=jhkgh&state=kjhdafg

OAuth2 Token Validation Service: POST /check_token

An endpoint that allows a resource server such as the cloud controller to validate an access token. Interactions between the resource server and the authorization provider are not specified in OAuth2, so we are adding this endpoint. The request should be over SSL and use basic auth with the shared secret between the UAA and the resource server (which is stored as a client app registration). The POST body should be the access token and the response includes the userID, user_name and scope of the token in json format. The client (not the user) is authenticated via basic auth for this call.

OAuth2 access tokens are opaque to clients, but can be decoded by resource servers to obtain all needed information such as userID, scope(s), lifetime, user attributes. If the token is encrypted witha shared sceret between the UAA are resource server it can be decoded without contacting the UAA. However, it may be useful -- at least during development -- for the UAA to specify a short, opaque token and then provide a way for the resource server to return it to the UAA to validate and open. That is what this endpoint does. It does not return general user account information like the /userinfo endpoint, it is specifically to validate and return the information represented by access token that the user presented to the resource server.

This endpoint mirrors the OpenID Connect /check_id endpoint, so not very RESTful, but we want to make it look and feel like the others. The endpoint is not part of any spec, but it is a useful tool to have for anyone implementing an OAuth2 Resource Server.

  • Request: uses basic authorization with base64(resource_server:shared_secret) assuming the caller (a resource server) is actually also a registered client:

    POST /check_token HTTP/1.1
    Host: server.example.com
    Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
    Content-Type: application/x-www-form-encoded
    
    token=eyJ0eXAiOiJKV1QiL
  • Successful Response:

    HTTP/1.1 200 OK
    Content-Type: application/json
    
    {
        "jti":"4657c1a8-b2d0-4304-b1fe-7bdc203d944f",
        "aud":["openid","cloud_controller"],
        "scope":["read"],
        "email":"marissa@test.org",
        "exp":138943173,
        "user_id":"41750ae1-b2d0-4304-b1fe-7bdc24256387",
        "user_name":"marissa",
        "client_id":"vmc"
    }

Notes:

  • The user_name is the same as you get from the OpenID Connect /userinfo endpoint. The user_id field is the same as you would use to get the full user profile from /Users.
  • Many of the fields in the response are a courtesy, allowing the caller to avoid further round trip queries to pick up the same information (e.g. via the /Users endpoint).
  • The aud claim is the resource ids that are the audience for the token. A Resource Server should check that it is on this list or else reject the token.
  • The client_id data represent the client that the token was granted for, not the caller. The value can be used by the caller, for example, to verify that the client has been granted permission to access a resource.
  • Error Responses: see OAuth2 Error responses and this addition:

    HTTP/1.1 400 Bad Request
    Content-Type: application/json;charset=UTF-8
    Cache-Control: no-store
    Pragma: no-cache
    
    { "error":"invalid_token" }

OAuth2 Token Endpoint: POST /oauth/token

An OAuth2 defined endpoint which accepts authorization code or refresh tokens and provides access_tokens. The access_tokens can then be used to gain access to resources within a resource server.

  • Request: POST /oauth/token
Request POST /oauth/token

Request Body

the authorization code (form encoded), e.g.:

code=F45jH
Response Codes 200 OK

Response Body

{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"bearer",
"expires_in":3600,
}

Support for additional authorization attributes

Additional user defined claims can be added to the token by sending them in the token request. The format of the request is as follows:

authorities={"additionalAuthorizationAttributes":{"external_group":"domain\\group1",
                                                  "external_id":"abcd1234"}}

A sample password grant request is as follows:

POST /uaa/oauth/token HTTP/1.1
Host: localhost:8080
Accept: application/json
Authorization: Basic YXBwOmFwcGNsaWVudHNlY3JldA==
"grant_type=password&username=marissa&password=koala&authorities=%
 7B%22additionalAuthorizationAttributes%22%3A%7B%22external_group%22%3A%22domain%
 5C%5Cgroup1%22%2C%20%22external_id%22%3A%22abcd1234%22%7D%7D%0A"

The access token will contain an az_attr claim like:

"az_attr":{"external_group":"domain\\group1","external_id":"abcd1234"}}

These attributes can be requested in an authorization code flow as well.

OpenID Check ID Endpoint: POST /check_id

An OpenID Connect defined endpoint. It accepts an id_token, which contains claims about the authentication event. It validates the token and returns information contained in the token in JSON format. Basically makes it so that clients do not need to have full token handling implementations.

Request POST /check_id
Request Body id_token=LKFJHDSG567TDFHG

OpenID User Info Endpoint: GET /userinfo

An OAuth2 protected resource and an OpenID Connect endpoint. Given an appropriate access_token, returns information about a user. Defined fields include various standard user profile fields. The response may include other user information such as group membership.

Request GET /userinfo
Response {"user_id":"olds","email":"olds@vmare.com"}

Login Information API: GET /login

An endpoint which returns login information, e.g prompts for authorization codes or one-time passwords. This allows vmc to determine what login information it should collect from the user.

This call will be unauthenticated.

Request GET /login_info or GET /login
Request body empty

Response body

example :

HTTP/1.1 200 OK
Content-Type: application/json

"prompt": {
    "email":["text", "validated email address"],
    "password": ["password", "your UAA password" ]
    "otp":["password", "security code"],
}

User Account Management APIs

UAA supports the SCIM standard for these APIs and endpoints. These endpoints are themselves secured by OAuth2, and access decision is done based on the 'scope' and 'aud' fields of the JWT OAuth2 token.

Create a User: POST /Users

See SCIM - Creating Resources

  • Request: POST /Users
  • Request Headers: Authorization header containing an OAuth2 bearer token with:

    scope = scim.write
    aud = scim
  • Request Body:

    {
      "schemas":["urn:scim:schemas:core:1.0"],
      "userName":"bjensen",
      "name":{
        "formatted":"Ms. Barbara J Jensen III",
        "familyName":"Jensen",
        "givenName":"Barbara"
      }
    }

The userName is unique in the UAA, but is allowed to change. Each user also has a fixed primary key which is a UUID (stored in the id field of the core schema).

  • Response Body:

    HTTP/1.1 201 Created
    Content-Type: application/json
    Location: https://example.com/v1/User/uid=123456
    ETag: "0"
    
    {
      "schemas":["urn:scim:schemas:core:1.0"],
      "id":"123456",
      "externalId":"bjensen",
      "meta":{
        "version":0,
        "created":"2011-08-01T21:32:44.882Z",
        "lastModified":"2011-08-01T21:32:44.882Z"
      },
      "name":{
        "formatted":"Ms. Barbara J Jensen III",
        "familyName":"Jensen",
        "givenName":"Barbara"
      },
      "userName":"bjensen"
    }
  • Response Codes:

    201 - Created successfully
    400 - Bad Request (unparseable, syntactically incorrect etc)
    401 - Unauthorized

Update a User: PUT /Users/{id}

See SCIM - Modifying with PUT

  • Request: PUT /Users/{id}
  • Request Headers: Authorization header containing an OAuth2 bearer token with:

    scope = scim.write
    aud = scim
  • Request Body:

    Host: example.com
    Accept: application/json
    Authorization: Bearer h480djs93hd8
    If-Match: "2"
    
    {
      "schemas":["urn:scim:schemas:core:1.0"],
      "id":"123456",
      "userName":"bjensen",
      "externalId":"bjensen",
      "name":{
        "formatted":"Ms. Barbara J Jensen III",
        "familyName":"Jensen",
        "givenName":"Barbara",
        "middleName":"Jane"
    
      },
      "emails":[
        {
            "value":"bjensen@example.com"
        },
        {
            "value":"babs@jensen.org"
        }
      ],
      "meta":{
        "version":2,
        "created":"2011-11-30T21:11:30.000Z",
        "lastModified":"2011-12-30T21:11:30.000Z"
      }
    }
  • Response Body:

    As for create operation, returns the entire, updated record, with the Location header pointing to the resource.

  • Response Codes:

    200 - Updated successfully
    400 - Bad Request
    401 - Unauthorized
    404 - Not found

    Note: SCIM also optionally supports partial update using PATCH.

Change Password: PUT /Users/{id}/password

See SCIM - Changing Password

  • Request: PUT /Users/{id}/password
  • Request Headers: Authorization header containing an OAuth2 bearer token with:

    scope = password.write
    aud = password

    OR :

    user_id = {id} i.e id of the user whose password is being updated
  • Request Body:

    Host: example.com
    Accept: application/json
    Authorization: Bearer h480djs93hd8
    
    {
      "schemas":["urn:scim:schemas:core:1.0"],
      "password": "newpassword",
      "oldPassword": "oldpassword"
    }
  • Response Body: the updated details
  • Response Codes:

    200 - Updated successfully
    400 - Bad Request
    401 - Unauthorized
    404 - Not found

Note

SCIM specifies that a password change is a PATCH, but since this isn't supported by many clients, we have used PUT. SCIM offers the option to use POST with a header override - if clients want to send X-HTTP-Method-Override they can ask us to add support for that.

Query for Information: GET /Users

See SCIM - List/Query Resources

Get information about a user. This is needed by to convert names and email addresses to immutable ids, and immutable ids to display names. The implementation provides the core schema from the specification, but not all attributes are handled in the back end at present (e.g. only one email address per account).

Filters: note that, per the specification, attribute values are comma separated and the filter expressions can be combined with boolean keywords ("or" and "and").

  • Request: GET /Users?attributes={requestedAttributes}&filter={filter}
  • Request Headers: Authorization header containing an OAuth2 bearer token with:

    scope = scim.read
    aud = scim
  • Response Body (for GET /Users?attributes=id&filter=emails.value eq bjensen@example.com):

    HTTP/1.1 200 OK
    Content-Type: application/json
    
    {
      "totalResults":1,
      "schemas":["urn:scim:schemas:core:1.0"],
      "resources":[
        {
          "id":"123456"
        }
      ]
    }
  • Response Codes:

    200 - Success
    400 - Bad Request
    401 - Unauthorized

Delete a User: DELETE /Users/{id}

See SCIM - Deleting Resources.

  • Request: DELETE /Users/{id}
  • Request Headers:
    • Authorization header containing an OAuth2 bearer token with:

      scope = scim.write
      aud = scim
    • If-Match the ETag (version id) for the value to delete
  • Request Body: Empty
  • Response Body: Empty
  • Response Codes:

    200 - Success
    401 - Unauthorized
    404 - Not found

Deleting accounts is handled in the back end logically using the active flag, so to see a list of deleted users you can filter on that attribute (filters by default have it set to true), e.g.

  • Request: GET /Users?attributes=id,userName&filter=userName co 'bjensen' and active eq false
  • Response Body: list of users matching the filter

Converting UserIds to Names

There is a SCIM-like endpoint for converting usernames to names, with the same filter and attribute syntax as /Users. It must be supplied with a filter parameter. It is a special purpose endpoint for use as a user id/name translation api, and is should be disabled in production sites by setting scim.userids_enabled=false in the UAA configuration. It will be used by vmc so it has to be quite restricted in function (i.e. it's not a general purpose groups or users endpoint). Otherwise the API is the same as /Users.

  • Request: GET /ids/Users
  • Response Body: list of users matching the filter

Query the strength of a password: POST /password/score

The password strength API is not part of SCIM but is provided as a service to allow user management applications to use the same password quality checking mechanism as the UAA itself. Rather than specifying a set of rules based on the included character types (upper and lower case, digits, symbols etc), the UAA exposes this API which accepts a candidate password and returns a JSON message containing a simple numeric score (between 0 and 10) and a required score (one which is acceptable to the UAA). The score is based on a calculation using the ideas from the zxcvbn project.

The use of this API does not guarantee that a password is strong (it is currently limited to English dictionary searches, for example), but it will protect against some of the worst choices that people make and will not unnecessarily penalise strong passwords. In addition to the password parameter itself, the client can pass a comma-separated list of user-specific data in the userData parameter. This can be used to pass things like the username, email or other biographical information known to the client which should result in a low score if it is used as part of the password.

  • Request: POST /password/score

    POST /password/score HTTP/1.1 Host: uaa.example.com Content-Type: application/x-www-form-encoded

    password=password1&userData=jane,janesdogsname,janescity

  • Response

    HTTP/1.1 200 OK Content-Type: application/json

    {"score": 0, "requiredScore": 5}

Group Management APIs

In addition to SCIM users, UAA also supports/implements SCIM_groups for managing group-membership of users. These endpoints too are secured by OAuth2 bearer tokens.

Create a Group: POST /Group

See SCIM - Creating Resources

  • Request: POST /Group
  • Request Headers: Authorization header containing an OAuth2 bearer token with:

    scope = scim.write
    aud = scim
  • Request Body:

    {
      "schemas":["urn:scim:schemas:core:1.0"],
      "displayName":"uaa.admin",
      "members":[
      { "type":"USER","authorities":["READ"],"value":"3ebe4bda-74a2-40c4-8b70-f771d9bc8b9f" }
    ]

    }

The displayName is unique in the UAA, but is allowed to change. Each group also has a fixed primary key which is a UUID (stored in the id field of the core schema).

  • Response Body:

    HTTP/1.1 201 Created
    Content-Type: application/json
    Location: https://example.com/v1/Group/uid=123456
    ETag: "0"
    
    {
      "schemas":["urn:scim:schemas:core:1.0"],
      "id":"123456",
      "meta":{
        "version":0,
        "created":"2011-08-01T21:32:44.882Z",
        "lastModified":"2011-08-01T21:32:44.882Z"
      },
      "displayName":"uaa.admin",
      "members":[
      { "type":"USER","authorities":["READ"],"value":"3ebe4bda-74a2-40c4-8b70-f771d9bc8b9f" }
      ]
    }
  • Response Codes:

    201 - Created successfully
    400 - Bad Request (unparseable, syntactically incorrect etc)
    401 - Unauthorized

The members.value sub-attributes MUST refer to a valid SCIM resource id in the UAA, i.e the UUID of an existing SCIM user or group.

Update a Group: PUT /Group/{id}

See SCIM - Modifying with PUT

  • Request: PUT /Group/{id}
  • Request Headers:
    • Authorization header containing an OAuth2 bearer token with:

      scope = scim.write OR groups.update
      aud = scim

      OR :

      user_id = <id of a user who is an admin member of the group being updated>
    • (optional) If-Match the ETag (version id) for the value to update
  • Request Body:

    Host: example.com
    Accept: application/json
    Authorization: Bearer h480djs93hd8
    If-Match: "2"
    
    {
      "schemas":["urn:scim:schemas:core:1.0"],
      "id":"123456",
      "displayName":"uaa.admin",
      "meta":{
        "version":2,
        "created":"2011-11-30T21:11:30.000Z",
        "lastModified":"2011-12-30T21:11:30.000Z"
      },
      "members":[
         {"type":"USER","authorities":["READ"],"value":"3ebe4bda-74a2-40c4-8b70-f771d9bc8b9f"},
         {"type":"USER","authorities":["READ", "WRITE"],"value":"40c44bda-8b70-f771-74a2-3ebe4bda40c4"}
      ]      
    }
  • Response Body:

    As for create operation, returns the entire, updated record, with the Location header pointing to the resource.

  • Response Codes:

    200 - Updated successfully
    400 - Bad Request
    401 - Unauthorized
    404 - Not found

As with the create operation, members.value sub-attributes MUST refer to a valid SCIM resource id in the UAA, i.e the UUID of a an existing SCIM user or group.

Note: SCIM also optionally supports partial update using PATCH, but UAA does not currently implement it.

Query for Information: GET /Groups

See SCIM - List/Query Resources

Get information about a group, including its members and what roles they hold within the group itself, i.e which members are group admins vs. which members are just members, and so on.

Filters: note that, per the specification, attribute values are comma separated and the filter expressions can be combined with boolean keywords ("or" and "and").

  • Request: GET /Groups?attributes={requestedAttributes}&filter={filter}
  • Request Headers: Authorization header containing an OAuth2 bearer token with:

    scope = scim.read
    aud = scim
  • Response Body (for GET /Groups?attributes=id&filter=displayName eq uaa.admin):

    HTTP/1.1 200 OK
    Content-Type: application/json
    
    {
      "totalResults":1,
      "schemas":["urn:scim:schemas:core:1.0"],
      "resources":[
        {
          "id":"123456"
        }
      ]
    }
  • Response Codes:

    200 - Success
    400 - Bad Request
    401 - Unauthorized

Delete a Group: DELETE /Group/{id}

See SCIM - Deleting Resources.

  • Request: DELETE /Group/{id}
  • Request Headers:
    • Authorization header containing an OAuth2 bearer token with:

      scope = scim.write
      aud = scim
    • If-Match the ETag (version id) for the value to delete
  • Request Body: Empty
  • Response Body: Empty
  • Response Codes:

    200 - Success
    401 - Unauthorized
    404 - Not found

Deleting a group also removes the group from the 'groups' sub-attribute on users who were members of the group.

Access Token Administration APIs

OAuth2 protected resources which deal with listing and revoking access tokens. To revoke a token with DELETE clients need to provide a jti (token identifier, not the token value) which can be obtained from the token list via the corresponding GET. This is to prevent token values from being logged in the server (DELETE does not have a body).

List Tokens for User: GET /oauth/users/{username}/tokens

  • Request: GET /oauth/users/{username}/tokens
  • Request body: empty
  • Response body: a list of access tokens, example :

    HTTP/1.1 200 OK
    Content-Type: text/plain
    
    [
      {
        "access_token": "FYSDKJHfgdUydsFJSHDFKAJHDSF",
        "jti": "fkjhsdfgksafhdjg",
        "expires_in": 1234,
        "client_id": "vmc"
      }
    ]

Revoke Token by User: DELETE /oauth/users/{username}/tokens/{jti}

  • Request: DELETE /oauth/users/{username}/tokens/{jti}
  • Request body: empty
  • Response code: 200 OK
  • Response body: a status message (hash)

List Tokens for Client: GET /oauth/clients/{client_id}/tokens

  • Request: GET /oauth/clients/{client_id}/tokens
  • Request body: empty
  • Response body: a list of access tokens, example :

    HTTP/1.1 200 OK
    Content-Type: text/plain
    
    [
      {
        "access_token": "KJHDGFKDHSJFUYTGUYGHBKAJHDSF",
        "jti": "fkjhsdfgksafhdjg",
        "expires_in": 1234,
        "client_id": "www"
      }
    ]

Revoke Token by Client: DELETE /oauth/clients/{client_id}/tokens/{jti}

  • Request: DELETE /oauth/clients/{client_id}/tokens/{jti}
  • Request body: empty
  • Reponse code: 200 OK
  • Response body: a status message (hash) :

    HTTP/1.1 200 OK
    { "status": "ok" }

Get the Token Signing Key: GET /token_key

An endpoint which returns the JWT token key, used by the UAA to sign JWT access tokens, and to be used by authorized clients to verify that a token came from the UAA.

This call is authenticated with client credentials using the HTTP Basic method.

Request GET /token_key
Request body empty

Response body

example :

HTTP/1.1 200 OK
Content-Type: text/plain

{alg:HMACSHA256, value:FYSDKJHfgdUydsFJSHDFKAJHDSF}

The algorithm ("alg") tells the caller how to use the value (it is the result of algorithm method in the Signer implementation used in the token endpoint). In this case it is an HMAC (symmetric) key, but you might also see an asymmetric RSA public key with algorithm "SHA256withRSA").

Client Registration Administration APIs

List Clients: GET /oauth/clients

Request GET /oauth/clients
Request body client details
Response code

200 OK if successful with client details in JSON response

Response body

example :

HTTP/1.1 200 OK
{foo: {
  client_id : foo,
  scope : [uaa.none]
  resource_ids : [none],
  authorities : [cloud_controller.read,cloud_controller.write,scim.read],
  authorized_grant_types : [client_credentials]
},
bar: {
  client_id : bar,
  scope : [cloud_controller.read,cloud_controller.write,openid],
  resource_ids : [none],
  authorities : [uaa.none],
  authorized_grant_types : [authorization_code]
}}

Inspect Client: GET /oauth/clients/{client_id}

Request GET /oauth/clients/{client_id}
Request body client details
Response code

200 OK if successful with client details in JSON response

Response body

example:

HTTP/1.1 200 OK
{
  client_id : foo,
  scope : [uaa.none],
  resource_ids : [none],
  authorities : [cloud_controller.read,cloud_controller.write,scim.read],
  authorized_grant_types : [client_credentials]
}

Register Client: POST /oauth/clients/{client_id}

Request POST /oauth/clients/{client_id}
Request body client details
Response code

201 CREATED if successful

Response body the client details

Example request:

POST /oauth/clients/foo
{
  client_id : foo,
  client_secret : fooclientsecret, // optional for untrusted clients
  scope : [uaa.none],
  resource_ids : [none],
  authorities : [cloud_controller.read,cloud_controller.write,openid],
  authorized_grant_types : [client_credentials],
  access_token_validity: 43200
}

(Also available for grant types that support it: refresh_token_validity.)

Update Client: PUT /oauth/clients/{client_id}

Request PUT /oauth/clients/{client_id}
Request body client details
Response code 200 OK if successful
Response body the updated details

Example:

PUT /oauth/clients/foo
{
  client_id : foo,
  scope : [uaa.none],
  resource_ids : [none],
  authorities : [cloud_controller.read,cloud_controller.write,openid],
  authorized_grant_types : [client_credentials]
}

N.B. the secret will not be changed, even if it is included in the request body (use the secret change endpoint instead).

Delete Client: DELETE /oauth/clients/{client_id}

Request DELETE /oauth/clients/{client_id}
Request body empty
Response code 200 OK
Response body the old client

Change Client Secret: PUT /oauth/clients/{client_id}/secret

Request PUT /oauth/clients/{client_id}/secret
Request body secret change request
Reponse code 200 OK if successful
Response body a status message (hash)

Example:

PUT /oauth/clients/foo/secret
{
  oldSecret: fooclientsecret,
  secret: newclientsceret
}

UI Endpoints

Web app clients need UI endpoints for the OAuth2 and OpenID redirects. Clients that do not ask for a JSON content type will get HTML. Note that these UIs are whitelabeled and the branded versions used in Cloud Foundry are deployed in a separate component (the Login Server).

Internal Login Form: GET /login

  • Request: GET /login?error={error}
  • Response Body: form with all the relevant prompts
  • Response Codes: 200 - Success

Internal Login: POST /login.do

  • Request: POST /login.do
  • Request Body, example -- depends on configuration (e.g. do we need OTP / PIN / password etc.):

    username={username}&password={password}...
  • Response Header, includes location if redirect, and cookie for subsequent interaction (e.g. authorization):

    Location: http://myapp.cloudfoundry.com/mycoolpage
    Set-Cookie: JSESSIONID=ldfjhsdhafgkasd
  • Response Codes:

    302 - Found
    200 - Success

Logout: GET /logout.do

The UAA can act as a Single Sign On server for the Cloud Foundry platform (and possibly user apps as well), so if a user logs out he logs out of all the apps.

OAuth2 Authorization Confirmation: GET /oauth/authorize/confirm_access

  • Request: GET /oauth/authorize/confirm_access
  • Request Body: HTML form posts back to /oauth/authorize:

    Do you approve the application "foo" to access your CloudFoundry
    resources with scope "read_cloudfoundry"? Approve/Deny.
  • Response Codes:

    200 - Success

OAuth2 Authorization: POST /oauth/authorize?user_oauth_approval=true

The precise form of this request is not given by the spec (which just says "obtain authorization"), but the response is.

  • Request: POST /oauth/authorize?user_oauth_approval=true
  • Request Header (needed to ensure the currently authenticated client is the one that is authorizing):

    Cookie: JSESSIONID=ldfjhsdhafgkasd
  • Response Header: location as defined in the spec (e.g. includes auth code for that grant type, and error information)
  • Response Codes:

    302 - Found

External Hosted Login Form (OpenID): GET /login

Request GET /login
Response Code 302 - Found

Response Headers

Location: http://www.google.com/etc/blah
Set-Cookie: JSESSIONID=ldfjhsdhafgkasd

Management Endpoints

Basic Metrics: GET /varz

Authentication is via HTTP basic using credentials that are configured via varz.username and varz.password. The /varz endpoint pulls data out of the JMX MBeanServer, exposing selected nuggets directly for ease of use, and providing links to more detailed metrics.

  • Request: GET /varz
  • Response Body:

    {
      "type": "UAA",
      "links": {
        "Users": "http://localhost:8080/uaa/varz/Users",
        "JMImplementation": "http://localhost:8080/uaa/varz/JMImplementation",
        "spring.application": "http://localhost:8080/uaa/varz/spring.application",
        "com.sun.management": "http://localhost:8080/uaa/varz/com.sun.management",
        "Catalina": "http://localhost:8080/uaa/varz/Catalina",
        "env": "http://localhost:8080/uaa/varz/env",
        "java.lang": "http://localhost:8080/uaa/varz/java.lang",
        "java.util.logging": "http://localhost:8080/uaa/varz/java.util.logging"
      },
      "mem": 19173496,
      "memory": {
        "verbose": false,
        "non_heap_memory_usage": {
          "max": 184549376,
          "committed": 30834688,
          "init": 19136512,
          "used": 30577744
        },
        "object_pending_finalization_count": 0,
        "heap_memory_usage": {
          "max": 902299648,
          "committed": 84475904,
          "init": 63338496,
          "used": 19173496
        }
      },
      "token_store": {
        "refresh_token_count": 0,
        "access_token_count": 0,
        "flush_interval": 1000
      },
      "audit_service": {
        "user_authentication_count": 0,
        "user_not_found_count": 0,
        "principal_authentication_failure_count": 1,
        "principal_not_found_count": 0,
        "user_authentication_failure_count": 0
      },
      "spring.profiles.active": []
    }

Detailed Metrics: GET /varz/{domain}

More detailed metrics can be obtained from the links in /varz. All except the env link (the OS env vars) are just the top-level domains in the JMX MBeanServer. In the case of Catalina there are some known cycles in the object graph which we avoid by restricting the result to the most interesting areas to do with request processing.

  • Request: GET /varz/{domain}
  • Response Body (for domain=Catalina):

    {
      "global_request_processor": {
        "http-8080": {
          "processing_time": 0,
          "max_time": 0,
          "request_count": 0,
          "bytes_sent": 0,
          "bytes_received": 0,
          "error_count": 0,
          "modeler_type": "org.apache.coyote.RequestGroupInfo"
        }
      }
    }

Beans from the Spring application context are exposed at /varz/spring.application.

# find src/main/webapp/WEB-INF -name \*.xml -exec grep '\${.*}' {} /dev/null \; | sed -e 's/.*\${\(.*\)}.*/\1/g' | sed -e 's/:/: /' | sed -e 's/\./:\n /'
# find src/main/webapp/WEB-INF -name \*.xml -exec grep '\#{.*}' {} /dev/null \;
uaa:
url: http://localhost:8080/uaa
clientinfo:
url: http://localhost:8080/uaa/clientinfo
approvals:
url: http://localhost:8080/uaa/approvals
login:
url: http://localhost:8080/uaa/authenticate
token:
url: http://localhost:8080/uaa/oauth/token
login:
authorize:
url: http://localhost:8080/login/oauth/authorize
autologin:
expire_when_used: true
links:
home: https://www.cloudfoundry.com
passwd: https://www.cloudfoundry.com/passwd
signup: https://www.cloudfoundry.com/signup
LOGIN_SECRET: loginsecret
require_https: false
dump_requests: false
# find uaa/src/main/webapp/WEB-INF/spring* -name \*.xml -exec grep '\${.*}' {} /dev/null \; | sed -e 's/.*\${\(.*\)}.*/\1/g' | sed -e 's/:/: /' | sed -e 's/\./:\n /'
# find uaa/src/main/webapp/WEB-INF/spring* -name \*.xml -exec grep '\#{.*}' {} /dev/null \;
spring_profiles: postgresql # default is empty
login.addnew: false # true in default profile
database:
driverClassName: org.postgresql.Driver # only if spring_profiles=postgresql
url: jdbc:postgresql:uaa # only if spring_profiles=postgresql
username: root # only if spring_profiles=postgresql
password: changeme # only if spring_profiles=postgresql
password-policy.required-score: 0
jwt:
token:
signing-key: tokenkey
verification-key: tokenkey
issuer.uri: http://localhost:8080/uaa
scim:
delete:
deactivate: false
userids_enabled: false # true in default profile
user:
override: false
users: # example only, default is empty
- marissa|koala|marissa@test.org|Marissa|Bloggs|uaa.user
userids_enabled: true
users:
group_membership: # example only, default is empty
- acme|acme.dev,acme.qa
- acme.dev|marissa
oauth:
authorize:
ssl:
client:
override: true
autoapprove:
- vmc
clients:
# example only (default is empty except in default profile)
login:
id: login
secret: loginsecret
scope: openid
authorized-grant-types: client_credentials,authorization_code
authorities: oauth.login
require_https: false
dump_requests: false

UAA Operator Issues

25 Feb 2013

uaa.yml

“things are the way they are because they got that way” – Gerald Weinberg

Let's walk through this file:
https://github.com/vmware-ac/portal2/blob/master/config/backend/uaa.yml

configuring secrets and keys

  • configuring the uaa with public key:

https://github.com/vmware-ac/deployments/blob/master/staging/staging.yml#L818

  • avoiding the phantom sre user problem

  • bootstrap clients and secrets

  • bootstrap users and group membership

  • override issues

  • dev-template => manifest.yml => uaa.yml.erb => uaa.yml => spring *.xml => uaa

running the uaa locally

  • mvn clean install; cd uaa/uaa; mvn tomcat7:run

  • or mvnDebug tomcat7:run

  • running a login server and uaa locally

    • from root with parallel checkouts of /uaa and /login-server
    • cd uaa
    • mvn clean install
    • cd ../login-server
    • mvn clean install
    • mvn tomcat:run -P integration

checking health with uaac

  • targets and contexts

  • info -- commit id, links

  • varz

  • be the client

  • decode tokens

  • be a user whoami, context

logs

  • typical tomcat, spring logs

  • some exceptions for unnecessary database migrations are logged and can be ignored

  • making sense of it all

uaa db

  • default is in memory

  • can specify postgres, soon mysql

testing the uaa

login server

  • another java app

  • primarily provides the oauth2 /authorize endpoint services, but sometimes proxies other endpoints to the uaa

  • contains any ui related to authentication and approvals

  • uaa has small, generic login server built In, but the login_server job provides CF artwork and UI

  • no db, just talks to the uaa

  • debug and deploy much like the uaa

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment