Creating a JSON Web Encryption (JWE) login response
Create an encrypted login response and configure the concatenation key derivation function (Concat KDF).
Overview
The identity provider (IdP) creates a login response, which is a JWE that’s encrypted according to RFC 7516 Section 5.1 with JWE Compact Serialization. The supported key agreement algorithm is ECDH-ES per RFC 7518 Section 4.6. See the following section for a description of the concatenation key derivation function (Concat KDF) inputs.
Key agreement uses the public key for the UserDeviceEncryptionKey that’s exchanged during registration. The supported encryption algorithm is A256GCM per RFC 7518 Section 5.3.
Configure Concat KDF
RFC 7518 Section 4.6.2 and NIST.800-56A sections 5.8.1 and 6.2.2.2 describe the configuration of Concat KDF for Platform SSO. See the table below for input definitions. The system concatenates the values per NIST.800-56A sections 5.8.1, and then computes a SHA-256 hash that it uses to create the key. The login response header needs to contain the PartyUInfo.
Key | Value | Notes |
|---|---|---|
| Set to the octets of the ASCII representation of the enc (algorithm) Header Parameter value. | Per RFC 7518 Section 4.6.2 when using direct key agreement |
| ` | |
| This needs to use the | |
| Set to the number of bits in the desired output key. | Per RFC 7518 Section 4.6.2 |
|
| Per RFC 7518 Section 4.6.2 |
Here’s an example of the Concat KDF inputs:
- Reps
1for a 256-bit key:00000001- Z
The exchanged key:
3491708C92422BB807EDF2B8183A42737C5DAA6C39BA9535321D51C836D7ADA1
- AlgorithmID
encfrom header:A256GCM, ASCII encoded
Length || data = 00000007||4132353647434D
- PartyUInfo
Prefix:
APPLE, UTF8 Encoded
Length || data = 00000005||4150504C45
Ephemeral Public Key (EPK):
“epk” : {
`“y” : “jbOoWZbgDFTEfLa1O_7ZuJy3R8d2XAw0CHWUKmJLsbU”,`
`“x” : “BkFHRYQoleq39LplGqlcmsEdnw64w0wbcbHAEjrM4pw”,`
`“kty” : “EC”,`
`“crv” : “P-256”`}
Length || data =
00000041||0406414745842895EAB7F4BA651AA95C9AC11D9F0EB8C34C1B71B1C0123ACCE29C8DB3A85996E00C54C47CB6B53BFED9B89CB747C7765C0C340875942A624BB1B5
Final partyUInfo length || data:
0000004E||000000054150504C45000000410406414745842895EAB7F4BA651AA95C9AC11D9F0EB8C34C1B71B1C0123ACCE29C8DB3A85996E00C54C47CB6B53BFED9B89CB747C7765C0C340875942A624BB1B5
Response JWE apu header is the base-64 URL encoded partyUInfo:
`AAAABUFQUExFAAAAQQSf34Uch3TYF27T8SpbtNWCjpKUSjHGDwoiUz8Yoh1nrQidjfO6iMRZIqrsNERt8JnlI2aUwAlZktJ8DZFGWKaB`````
- PartyVInfo
Prefix:
Apple, UTF8 Encoded Length | data =00000005||4170706C65
Device Encryption Public Key:
{
`“crv” : “P-256”,`
`“kty” : “EC”,`
`“x” : “mcJyr2BqUQHlscaGoWTw_4QNxDUqI1lR11kCRAzLJkk”,`
`“y” : “P_mLtZKoMMC3G8PtRleKzm1c5D0afPZX_61s70Cx75I”`}
Length || data =
00000041||0499C272AF606A5101E5B1C686A164F0FF840DC4352A235951D75902440CCB26493FF98BB592A830C0B71BC3ED46578ACE6D5CE43D1A7CF657FFAD6CEF40B1EF92
Nonce: B7F1FC32-9121-4E2A-9E32-8417E03675DD
Length || data =
00000024||42374631464333322D393132312D344532412D394533322D383431374530333637354444
Final partyVInfo length || data:
000000054||170706C65000000410499C272AF606A5101E5B1C686A164F0FF840DC4352A235951D75902440CCB26493FF98BB592A830C0B71BC3ED46578ACE6D5CE43D1A7CF657FFAD6CEF40B1EF920000002442374631464333322D393132312D344532412D394533322D383431374530333637354444
The login request jwe_crypto.apv header is the base 64 URL encoded partyVInfo:
`AAAABUFwcGxlAAAAQQSZwnKvYGpRAeWxxoahZPD_hA3ENSojWVHXWQJEDMsmST_5i7WSqDDAtxvD7UZXis5tXOQ9Gnz2V_-tbO9Ase-SAAAAJEI3RjFGQzMyLTkxMjEtNEUyQS05RTMyLTg0MTdFMDM2NzVERA```
- SuppPubInfo
256 bits in the output key =
00000100- SuppPrivInfo
NULL
The complete SHA-256 input is:
000000013491708C92422BB807EDF2B8183A42737C5DAA6C39BA9535321D51C836D7ADA1000000074132353647434D0000004E000000054150504C45000000410406414745842895EAB7F4BA651AA95C9AC11D9F0EB8C34C1B71B1C0123ACCE29C8DB3A85996E00C54C47CB6B53BFED9B89CB747C7765C0C340875942A624BB1B500000076000000054170706C65000000410499C272AF606A5101E5B1C686A164F0FF840DC4352A235951D75902440CCB26493FF98BB592A830C0B71BC3ED46578ACE6D5CE43D1A7CF657FFAD6CEF40B1EF920000002442374631464333322D393132312D344532412D394533322D38343137453033363735444400000100
The Concat KDF result is:
A146E4A23BDA2E53826C04D2F442BCFBD87BC2719D74B8A7DA00AF976267712E
Create the response JWE header
The response is a JWE that’s decrypted according to RFC 7516 Section 5.2 with JWE Compact Serialization.
The JWE response header contains the following values:
Key | Value | Notes |
|---|---|---|
|
| Required if the extension SDK is macOS 13.x. |
| Optional if the extension SDK is macOS 14.x or later. | |
|
| Required. The supported encryption algorithm per RFC 7518 Section 4.6. |
|
| Required. The supported key agreement algorithm per RFC 7518 Section 5.3. |
| The ephemeral public key for the key exchange | Required. Per RFC 7518 Section 4.6.1.1, formatted per RFC 7517. |
| The | Optional. |
| The base-64 URL encoded | Required. Per RFC 7518 Section 4.6.1.2. |
| The base-64 URL encoded | Optional. Per RFC 7518 Section 4.6.1.3, the client uses the value from the request. |
The following code provides an example of a login response JWE header:
{
"enc" : "A256GCM",
"kid" : "d20/VkvmbjLfmi1ufWQqSzzPx+CFHH4N/57Smjk34FE=",
"epk" : {
"y" : "doNOTncIN5cjSujnX2qiuHOFL-fag2iypIpDXx1eLdo",
"x" : "43ZFsDGBnttfDSiNBbsdmXnj3N9ieZaT0nEsEZHk7eo",
"kty" : "EC",
"crv" : "P-256"
},
"apu" : "AAAABUFQUExFAAAAQQTjdkWwMYGe218NKI0Fux2ZeePc32J5lpPScSwRkeTt6naDTk53CDeXI0ro519qorhzhS_n2oNosqSKQ18dXi3a",
"typ" : "platformsso-login-response+jwt",
"alg" : "ECDH-ES"
}Create the response JWE body
The IdP creates a successful login response formatted per the OpenID spec, including an id_token and refresh_token. The refresh_token is the token that the system uses for SSO.
The JWE response body contains the following values:
Key | Value | Notes |
|---|---|---|
| Base-64 URL encoded | Required. |
|
| Required. |
| The time (in seconds) until the refresh token expires. If not present, the system uses | Recommended. |
| The time in seconds until the tokens expire. | Required, if |
|
| Expected, but not verified |
Kerberos TGT | Kerberos TGT, if Kerberosticketmappings is defined. | Optional. |
The following code provides an example of a login response JWE body:
{
"refresh_token" : "AwABA...0t4B4",
"id_token" : "ewogI...lEvVQ",
"expires_on" : 1685766415,
"token_type" : "Bearer",
"expires_in" : 28800,
"refresh_token_expires_in" : 28800
}Handle a failed response
If the response returns a status code other than 200, Platform SSO checks whether there’s a credential failure. If the credential is incorrect, the system prompts the user to enter it again. Other errors require another login attempt at a later time.
By default, a HTTP response code of 401 is a credential error. The IdP can optionally specify a predicate to parse a JSON formatted response to determine if the result is a credential error or not, when the response code is 400 or 401. The invalidCredentialPredicate specifies the predicate.
For example, the system can use the predicate errorCode = ‘C0000006’ AND suberror = ‘invalid_password’ with the following failed response to match to a credential error:
HTTP/1.1 400 Bad Request
Cache-Control: no-store, no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
{
"errorCode": "C0000006",
"suberror": "invalid_password"
}Use the Kerberos mapping
The SSO response from the IdP can optionally include Kerberos ticket-granting tickets (TGTs) for the user. Platform SSO uses the kerberos mapping from the login configuration to import the TGTs into a Kerberos cache and optionally use the TGT with the Kerberos extension.
The Kerberos mapping is an instance of ASAuthorizationProviderExtensionKerberosMapping in the kerberosTicketMappings. It indicates the SSO response entries to use for each of the elements, with each ticket being a sub-dictionary of the login response. The mapping entry maps the response JSON entry names for each ticket.
The following code sample provides an example of a login response body:
{
"expires_in" : 3600,
"expires_on" : "1646867283.216107",
"id_token" : "ewogIC...hyI9w",
"refresh_token" : "abcd1234",
"token_type" : "Bearer",
"login_tgt" : {
"clientName" "foo",
"encryptionKeyType" : 18,
"messageBuffer" : "a4IGhDCCBoC...h3YEY6IQ==",
"realm" : "EXAMPLE.COM",
"serviceName" : "krbtgt/EXAMPLE.COM",
"sessionKey" : "EjzbGACRvT1WnSeBkQDnvevt7A7/MuGw0oEVAQRZutU="
}
}For the above login response, the Kerberos ticket mapping is:
Key | Value | Notes |
|---|---|---|
|
| The keypath in the response JSON that uses this set of mappings. |
|
| The key name that contains the base-64 encoded kerberos AS-REP string. |
|
| The key name that contains the Kerberos Realm string. |
|
| The key name that contains the Kerberos service name string. |
|
| The key name that contains the Kerberos client name string. |
|
| The key name that contains the Kerberos session key type number. The value for this key should be the correct encryption type per RFC 3962 Section 7 for the session key. |
|
| The key name that contains the Kerberos session key. |
The ASAuthorizationProviderExtensionKerberosMapping is:
var mapping = ASAuthorizationProviderExtensionKerberosMapping()
mapping.ticketKeyPath = "login_tgt"
mapping.clientNameKeyName = "clientName"
mapping.encryptionKeyTypeKeyName = "encryptionKeyType"
mapping.messageBufferKeyName = "messageBuffer"
mapping.realmKeyName = "realm"
mapping.serviceNameKeyName = "serviceName"
mapping.sessionKeyKeyName = "sessionKey"If the sytem finds all the values, it imports the ticket into a Kerberos cache that the Kerberos extension can use.
If the Kerberos ticket is from a read-only domain controller (RODC) per MS-KILE Section 3.1.5.8, a refresh request to the key distribution center (KDC) uses the ticket to exchange it for a TGT that contains a privileged attribute certificate (PAC).