Contents
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.
The login server component used to be separate from UAA. It has now been merged into the UAA so it can present an appropriate visual rendering of the login page and authentication interfaces. The login-server is now the login module in the UAA repository.
The login 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)
These apply if you are developing identity integration in your own application, outside a bosh deployment scenario.
Requirements:
java >= 17
Clone, build UAA server:
git clone git@github.com:cloudfoundry/uaa.git cd uaa ./gradlew clean assemble
Run Servers (using the UAA version <X> from above):
./gradlew run
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) named CLOUDFOUNDRY_CONFIG_PATH. The UAA will then look for a file named $CLOUDFOUNDRY_CONFIG_PATH/uaa.yml.
In addition to be able to override configuration through file based locations, complete Yaml can also be written as an environment variable. For a Cloud Foundry application this could look like.
---
applications:
- name: standalone-uaa-cf-war
memory: 4096M
instances: 1
host: standalone-uaa
path: cloudfoundry-identity-uaa-0.0.0-SNAPSHOT.war
env:
JBP_CONFIG_SPRING_AUTO_RECONFIGURATION: '[enabled: false]'
JBP_CONFIG_TOMCAT: '{tomcat: { version: 9.0.+ }}'
SPRING_PROFILES_ACTIVE: hsqldb
JAVA_OPTS: '-Djava.security.egd=file:/dev/./urandom -Dlogging.config=/usr/local/tomcat/conf/log4j2.properties'
UAA_CONFIG_YAML: |
uaa.url: http://standalone-uaa.cfapps.io
login.url: http://standalone-uaa.cfapps.io
smtp:
host: mail.server.host
port: 3535
Or as an alternative, set the yaml configuration as a string for an environment variable using the set-env command
cf set-env sample-uaa-cf-war UAA_CONFIG_YAML '{ uaa.url: http://uaa.myapp.com, login.url: http://uaa.myapp.com, smtp: { host: mail.server.host, port: 3535 } }'
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: passwordUAA supports either symmetric key encryption (shared secrets) or public key encryption, however it is strongly recommended to use public key encryption.
jwt:
token:
policy:
activeKeyId: key-id-1-or-better-a-UUID
keys:
key-id-1-or-better-a-UUID:
signingAlg: RS256
signingKey: …
signingCert: …
key-id-2-or-better-a-UUID:
signingAlg: RS512
signingKey: …If you want to use symmetric key encryption, signing and verification values should be the same and the signingAlg can be from HS256 to HS512. The public or asymmetric key encryption requires at least a signingKey. This key must be compatible to the signingAlg. If no parameter signingAlg is specified the default either RS256 or HS256 is taken. The algorithm none is not supported. This default depends on the key and its format. For other algorithms, e.g. see the supported ones in RFC 7518 (https://datatracker.ietf.org/doc/html/rfc7518#section-3.1), the signingKey must be compatible, e.g. ES256. If you set signingCert, then the token_key endpoint contain the x5c claim with the PEM encoded X509 representation and x5t with its thumbprint in order to have a unique identitfier. The value of x5c can be used as trust anchor towards OIDC Identity Proviers, e.g. Azure AD from Microsoft and SAP Cloud Identity Service uses x509 as trust anchor for JWT client authentication. If no signingCert is defined, then both claims does not appear in token_key response.
Generating new asymmetric key pairs for RSA based algorithm
mkdir temp_uaa_certs cd temp_uaa_certs openssl genrsa -out privkey.pem 2048 openssl rsa -pubout -in privkey.pem -out pubkey.pem
Generating new asymmetric key pairs for Elliptic Curve based algorithm, e.g. ES256
openssl ecparam -genkey -name prime256v1 -noout -out ec256-key-pair.pem
Aysmmetric key pairs can be set directly in the yaml file using block literals. Make sure the entire key is indented.
jwt:
token:
policy:
activeKeyId: key-id-1-or-better-a-UUID
keys:
key-id-1-or-better-a-UUID:
signingAlg: RS256
signingKey: |
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAyV3Ws3gLOvi169ZPx8v3t9UZpxcG0fqtQzC4X+Ff7dlx4w6P
...
pYPYK4M+4Gwi7O49a63G+lzX7BqUWYBXR84iZG+vWz2F3ICjiOIz
-----END RSA PRIVATE KEY-----
signingCert: |
-----BEGIN CERTIFICATE-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyV3Ws3gLOvi169ZPx8v3
...
XwIDAQAB
-----END PUBLIC KEY-----The key rotation is a change of parameter jwt.token.policy.activeKeyId. The key identifier of each entry appears in token_key response with claim kid. It is the identifier for key resolution, therefore this definition should be unique. Parameter activeKeyId specifies the current active key, which is used for signing the JWT based tokens (id_token, access_token, refresh_token), see UAA-Tokens.md. After the change of activeKeyId the replaced key should not be removed but it should be kept some time in order to allow a signature verification for clients for a period of time. If you use UAA as proxy to other OIDC Providers, then this communication can be protected with JWT authentication and the key for this JWT signing can be different to the activeKeyId.
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:
- cf
- support-signonIndividual client settings in uaa.yml go in sections under “clients” using the client name:
oauth:
clients:
portal:
name: Portal App
override: true
scope: openid,cloud_controller.read,cloud_controller.write
authorities: openid,cloud_controller.read,cloud_controller.writeOverride 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.
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.
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,openidA 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 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 cf can take a --trace option which shows each online interaction.
"uaac 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.
cf and uaac each need a target. cf points to a cloud controller and uaac to a uaa instance.
cf 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 cf 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.
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
The cf client can be used for user registrations:
cf create-user sre@vmware.com mypassword uaac users # examine all users uaac user ids # look up user ids -- only works outside production
Register a new user
uaac user add
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
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
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:
uaac signing key -c admin -s adminsecret
UAA documentation in docs/
- UAA-APIs.rst: API document, kept updated
- UAA-CC-ACM-CF-Interactions.rst: flows for operations between parts
- UAA-Overview.rst: comparisons with oauth2
- UAA-Security.md: accounts, bootstrapping, scopes for access control
- UAA_presentation.pdf: Overview presentation, outline for internal developers
- CF-Identity-Services-Preface.rst: justification and design overview
Login-server documentation in docs/
- Login-APIs.md: login-server specifics like autologin
- Hyperlink other documentation
- Link from main README
- Expand examples for tcpdump and debugging