Credential Service
A credential service can be used to provide credentials to perform single sign-on to protected applications. Single sign-on can be achieved using basic authentication or using a mechanism which automatically submits forms-based logins.
Credential Service
A credential service is a light-weight web service which implements two endpoints, one for retrieving credentials and another for storing credentials. At runtime, IAG will contact this service to retrieve the credentials when single sign-on is performed.
Note
Note that storing credentials is only performed when using forms-based login with credential learning enabled.
- Refer to the services/credential YAML reference for information about defining a credential service to IAG.
- Refer to identity_headers#basic_auth for configuring IAG to provide credentials from the credential service as basic authentication headers.
- Refer to forms_login for configuring IAG to complete forms-based logins using credentials from the credential service.
IBM Security Verify
IBM Security Verify provides a Password Vault API which can be used as a credential service with IBM Application Gateway. Refer to the Password Vault API reference at https://<hostname>.verify.ibm.com/developer/explorer
and Verify Application programming interfaces (APIs) for general information about IBM Security Verify APIs.
Example IBM Security Verify Credential Service
services:
credential:
- name: example_pwdvault
host: https://<hostname>.verify.ibm.com
url_pattern: /v1.0/pwdvault/{user}/resources/{resource}
user_attribute: uniqueSecurityName
user_attribute_encoding: url
enc_key: "@pwdvault-rsa.pem"
authentication:
sso:
client_id: 72eaxxxx-xxxx-xxxx-xxxx-xxxxxxxx5c1d
client_secret: y68xxxxBmw
endpoint: https://<hostname>.verify.ibm.com/v1.0/endpoint/default/token
payload: form
Note that IAG must use the OAuth client credential flow to authenticate with IBM Security Verify and access the Password Vault APIs. The client ID and secret must belong to an API client which has the managePwdvaultAnyUser
entitlement.
IBM Security Verify Access
The Advanced Access Control component of IBM Security Verify Access (version 10.0.1.0 or greater) provides a SSO service which can be used to store credentials in either the Verify Access user registry or the runtime database. Further information is contained in the Password Vault topic of the IBM Security Verify Access documentation.
Implementing your own Credential Service
Credential Service Endpoints
A credential service is configured by providing a URL pattern to IAG. The credential service URL must contain the '{resource}' and '{user}' placeholders. IAG will substitute in the resource name and effective username when making requests to this service.
- The resource name is specified in the resource server configuration which uses this credential service for single sign-on.
- The effective username is taken from a credential attribute. This can be configured for each credential service. The default credential attribute used is 'AZN_CRED_PRINCIPAL_NAME'.
The credential service must be able to respond to GET and PUT requests on the provided URL pattern when populated.
For example, a credential service with the following URL pattern '/credentials/resources/{resource}/users/{user}' must implement the following two endpoints:
Retrieving Credentials
To retrieve credentials, IAG will issue a GET request to the configured endpoint.
> GET /credentials/resources/{resource}/users/{user}
< 200 OK
< {
< "username": <username>,
< "password": <password>
< }
The endpoint must return at a minimum:
- Any HTTP status code indicating success (any status code beginning with 2xx)
- A JSON body containing the 'username' and 'password' fields. Any other fields will be ignored.
Storing Credentials
When storing credentials, IAG will issue a PUT request to the configured endpoint. The request will contain a JSON body with the 'username' and 'password' fields.
> PUT /credentials/resources/{resource}/users/{user}
> {
> "username": <username>,
> "password": <password>
> }
< 201 CREATED
The endpoint must return at a minimum:
- Any HTTP status code indicating success (any status code beginning with 2xx)
Encoding the '{user}' URL token
By default, when constructing URLs the '{user}' token is URL encoded. IAG can optionally convert the token to lower case and Base64URL encode it, which is recommended when '{user}' values contain characters which require percent-encoding. Refer to the property 'user_attribute_encoding' in the services/credential YAML reference.
When Base64URL encoding is enabled, IAG will indicate to the credential service that the '{user}' token is encoded by including a query string parameter 'encoding=base64url'.
For example, consider the URL generated for a '{resource}' named 'testResource' with a '{user}' token '星の白金':
## with user_attribute_encoding set to 'base64url':
GET /credentials/resources/testResource/users/5pif44Gu55m96YeR?encoding=base64url
## with user_attribute_encoding unset or set to 'url':
GET /credentials/resources/testResource/users/%E6%98%9F%E3%81%AE%E7%99%BD%E9%87%91
For example, consider the URL generated for a '{resource}' named 'testResource' with a '{user}' token '[email protected]':
## with user_attribute_encoding set to 'base64url':
## note that the token '[email protected]' is converted to the lower case
## representation '[email protected]' before the encoding takes place.
GET /credentials/resources/testResource/users/c2FtcGxlX3VzZXJfYWNjb3VudF8xQHRlc3QuY29t?encoding=base64url
## with user_attribute_encoding unset or set to 'url':
GET /credentials/resources/testResource/users/Sample_User_Account_1%40test.com
Encryption of Credentials
IAG stores credential passwords as JSON Web Encryption (JWE) tokens. An RSA or ECDSA key must be provided to IAG to perform the encryption operations.
Credential passwords provided by the credential service should conform the following standards used by IAG:
- The password string must begin with the '{jwe}' prefix to indicate that it is a JWE
- Following the '{jwe}' prefix must be a JWE representation of the password. This JWE:
- Must use a 'kid' value which is the label of the certificate IAG will use for decryption
- Must use the 'enc' value 'A256GCM'
- For RSA keys, must use one of the following encryption algorithms for 'alg': 'RSA1_5' or 'RSA_OAEP'
- For ECDSA keys, must use the following encryption algorithm for 'alg': 'ECDH-ES'
If IAG is returned credentials which do not contain the '{jwe}' prefix, they will be treated as in-the-clear and used as is. Storing or providing credentials to IAG in-the-clear is considered unsafe and should not be performed.
Sample IAG JWE Generator/Validator
The following script can be used be used to generate/validate encrypted IAG JWE credential password values.
##!/usr/bin/env python3
import json
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from jwcrypto.jwe import JWE
from jwcrypto.jwk import JWK
class IAGJWE(object):
def __init__(self, pem):
"""
This class can be used to generate/verify IAG JWEs.
:param pem: Path to the key which is used for crypto.
"""
# Load the pem data
with open(pem, "rb") as fh:
self.pem_data = fh.read()
# Create a JWK
self.jwk = JWK.from_pem(self.pem_data)
# Work out the KID
self.cert = x509.load_pem_x509_certificate(self.pem_data, backend=default_backend())
self.kid = self.cert.subject.rfc4514_string()
# Determine the algorithms which should be used
signature_algorithm = str(self.cert.signature_algorithm_oid)
if "ecdsa" in signature_algorithm:
self.jwe_alg = "ECDH-ES"
elif "RSA" in signature_algorithm:
self.jwe_alg = "RSA1_5"
else:
print("Invalid signature algorithm ({0})".format(signature_algorithm))
print("Use an RSA or ECDSA key.")
def generate_password_jwe(self, password):
"""
Generates an IAG password JWE.
:param password: The password to generate the JWE representation for.
:return: A string containing the JWE representation, in the
IAG "{jwe}xxx" format.
"""
# Produce the JWE
jwe = JWE(password.encode(), json.dumps({
"alg": self.jwe_alg,
"enc": "A256GCM",
"kid": self.kid
}))
jwe.add_recipient(self.jwk)
# Print it in the IAG credential service format
return "{jwe}" + jwe.serialize(compact=True)
def validate_password_jwe(self, raw_jwe):
"""
Validates an IAG password JWE.
:param raw_jwe: The IAG password JWE, in the IAG "{jwe}xxx" format.
:return: The decrypted password.
"""
# Deserialize and decrypt the JWE
jwe = JWE()
jwe_payload = jwe.deserialize(raw_jwe[5:], key=self.jwk)
return jwe.plaintext.decode()
def usage():
print("Usage: {0} validate <key> <string>".format(sys.argv[0]))
print("Usage: {0} generate <key> <string>".format(sys.argv[0]))
exit(1)
if __name__ == "__main__":
import sys
if len(sys.argv) != 4:
usage()
if sys.argv[1] == "validate":
jwe = IAGJWE(sys.argv[2])
print(jwe.validate_password_jwe(sys.argv[3]))
elif sys.argv[1] == "generate":
jwe = IAGJWE(sys.argv[2])
print(jwe.generate_password_jwe(sys.argv[3]))
else:
usage()
exit(0)
kid validation
The JWE decryption performed by the IAG performs kid validation. The kid sent in the JWE must match the kid of the private key held by the IAG. If no kid is set for the certificate, the certificate label (friendly name) will be used. By default, the certificate label is set to the Subject DN of the certificate.
This validation is NOT performed by the example script above.
Updated almost 2 years ago