- OIDC Native SSO
This document specifies the implementation of OIDC Native SSO.
- A OfflineGrant used to has only one set of client-specific information, like
client_id
andrefresh_token
. Now, a OfflineGrant can have multiple refresh tokens. - Since there is only one OfflineGrant, the apps sharing user authentication with Native SSO does not share refresh token. But the underlying session is shared. Thus signing out in one app will sign out all apps. This is by design. See App2app if you want the apps have independent sessions.
- Native SSO is done through the Token Endpoint, thus it requires no user interaction.
- Existing OfflineGrant cannot perform Native SSO. It is because
device_sso
is not in thescope
. Sign in again to obtain a Native SSO OfflineGrant to perform Native SSO.
- Add
x_device_sso_enabled: boolean
. scope=device_sso
is allowed ifx_device_sso_enabled=true
.
Do we need to add
x_device_sso_key: string
to designate which group of clients can perform Native SSO? Only clients withx_device_sso_enabled=true
AND the same value ofx_device_sso_key
can perform Native SSO with each other. This seems very advanced to me.
- The following fields become client-specific
ClientID
AuthorizationID
CreatedAt
Scopes
TokenHash
- Add a new field
RefreshTokens
. It will store all of the above imformation per refresh token. - Add a new field
DeviceSecretHash
. It is the hex of SHA256 ofdevice_secret
. - All new offline grants will use the new
RefreshTokens
to store information about refresh tokens.
- Allow
scope=device_sso
if the client hasx_device_sso_enabled=true
.
- If
authorization_code.scope=device_sso
anddevice_secret
is present and it is valid, a newrefresh_token
is added to Native SSO offline grant. - If
authorization_code.scope=device_sso
anddevice_secret
is absent or it is invalid, a new Native SSO offline grant is created.
- If
refresh_token.scope=device_sso
anddevice_secret
is present and it is valid, nothing to do. - If
refresh_token.scope=device_sso
anddevice_secret
is absent or it is invalid, a newdevice_secret
is generated.DeviceSecretHash
is updated.
- Support a new parameter
scope
. - Allow
scope=device_sso
if the client hasx_device_sso_enabled=true
. - If
scope=device_sso
anddevice_secret
is present and it is valid, a newrefresh_token
is added to Native SSO offline grant. - If
scope=device_sso
anddevice_secret
is absent or it is invalid, a new Native SSO offline grant is created.
Native SSO has no direct impact on app2app.
- Validate
scope=device_sso
. Returnerror=invalid_request
otherwise. This is because this is the only Token Change flow we support. - Validate
audience
is the origin of the endpoint. - Validate
subject_token
is a valid ID token issued to the first app. An expired ID token is still valid. (4.3 Point 2) - Validate
subject_token_type
isurn:ietf:params:oauth:token-type:id_token
. - Validate
actor_token
is a validdevice_secret
. (4.3 Point 1) - Validate
actor_token_type
isurn:x-oath:params:oauth:token-type:device-secret
. - Validate
requested_token_type
is absent. - Validate
subject_token.ds_hash
is the hex of SHA256 ofactor_token
. (4.3 Point 3) - Validate
subject_token.sid
is pointing to a valid session. (4.3 Point 4) - Validate
client_id
andsubject_token.aud
are allowed to perform Native SSO. (4.3 Point 5) - Validate
scope
is equal or a subset of thescope
in all refresh tokens of the Native SSO offline grant. (4.3 Point 6)
- If the offline grant has
DeviceSecretHash
, setds_hash
in the ID token. - Keep setting
sid
.
- Add
clientIDs
toSession
. Session.clientID
is the first client ID of a Native SSO offline grant.
- Rename
isSSOEnabled
toisBrowserSSOEnabled
inConfigureOptions
. - Add
isDeviceSSOEnabled
toConfigureOptions
. - If
isDeviceSSOEnabled
is true, thendevice_sso
is included in authorization requests. - Add
deviceSecretStore
. It is responsible for storingdevice_secret
andid_token
in Token Response. - If
device_secret
is found indeviceSecretStore
andisDeviceSSOEnabled
is true, thendevice_secret
is included in Token Request. - If
id_token
is present in Token Response, it is persisted intodeviceSecretStore
. - If
device_secret
is present in Token Response, it is persisted intodeviceSecretStore
. logout()
clearsdeviceSecretStore
.- Add
checkDeviceSSOPossible(): Promise<void>
. It throws error if eitherdevice_secret
orid_token
is not found indeviceSecretStore
. - Add
authenticateDeviceSSO(): Promise<UserInfo>
. - Expose
refreshToken
on Authgear.
Future works
- Add
IOSAppGroupDeviceSecretStorage
. - Add
AndroidAccountManagerDeviceSecretStorage
.
Recipe requires Future works to be done first.
- Configure both apps to use
IOSAppGroupDeviceSecretStorage
andAndroidAccountManagerDeviceSecretStorage
. - Configure both apps to set
isDeviceSSOEnabled
to true. - Sign in normally in App 1.
- In App 2, call
checkDeviceSSOPossible()
. It returns normally. - In App 2, if
checkDeviceSSOPossible()
returns normally, callauthenticateDeviceSSO()
. - In App 2, the end-user is authenticated. No user interaction is involved.
- Configure the app to set
isDeviceSSOEnabled
to true. - Sign in normally.
- To open a webapp, do the following
- Construct a new Container with
tokenStorage
set to TransientStorage, andisDeviceSSOEnabled
to true. The purpose of this is to prevent this Container from messing with the original Container. The new Container is now unauthenticated (due to TransientStorage) but hasdevice_secret
(due toisDeviceSSOEnabled
being true). - Call
authenticateDeviceSSO()
- Inject
refreshToken
of the new Container into the custom webview. This requires knowledge on the implementation details of the Web SDK. - The Container of the Web SDK considered itself as authenticated due to the injected refresh token.
- The iOS keychain will not be cleaned up even all apps in the app group were removed.
- Developer is required to provide the
accountType
to initialize the store. Applications must belong to the given app group or the SDK might malfunction. Developer must also define a<account-authenticator>
resource with the sameaccountType
, and a<service>
which uses the defined account authenticator in all apps that is sharing the authentication session. For details read this document. - The first installed app will be the "authenticator" app in the android, which in fact owns the accounts. Once the app was removed, the accounts will be removed together with the app.
device_secret should always be bound to a device.
OAuth 2.0 Demonstrating Proof of Possession is implemented for such binding.
The DPoP Proof MAY be provided when making requests to the /token endpoint. If DPoP Proof is provided, device_secret returned in the response will be bound to the public key provided in the DPoP Proof. Which the keypair used to sign such DPoP Proof is expected to be stored in a secure device storage such as iOS Keychain or Android Keystore.