Configuring an interaction hook
Many credential issuance journeys require the issuer to do some custom interactions with the user. This could be gathering additional information, performing additional authentication steps (E.g, 2FA, MFA or biometric checks) or communicating terms of service.
To facilitate this requirement, you can configure MATTR VII to invoke an interaction hook which will redirect the end user to a custom component during the credential issuance journey. This redirect happens after the user is authenticated but before the credential has been issued to the user. Upon the successful completion of this step, your custom component will redirect the end user back to MATTR VII to complete their credential issuance flow.
Your interaction hook component must be accessible by HTTPS URLs, and it could be either a web or native application. However, we recommend using a web interface because it's more compatible to most scenarios.
Setting up the interaction hook
This is an example to configure the interactions hook that allows the MATTR VII platform to redirect the user to your web server after completing the OpenID authentication process.
Make the following request .
Request
1PUT https://YOUR_TENANT_URL/v1/openid/configuration
1{
2 "interactionHook": {
3 "url": "https://example-university.com/callback",
4 "claims": [],
5 "disabled": false
6 }
7}
8
Here’s what each field of the request payload means:
url
: The interaction hook URL that the user will be redirected to after completing the OpenID authentication process.claims
: An array of user attributes that will be returned in the OpenID authentication response. These claims get included in the session token that is sent to the interaction hook. It is not recommended to use interaction hooks as a source of claims for the issuance of credentials. You should be using claim source as the primary source of claims.disabled
: A flag that indicates whether the interactions hook is disabled. In this case, it is set to false, indicating that the interactions hook is enabled. This means that users will be redirected to your interaction hook component (http://example-university.com/callback
) after they've authenticated themselves against the IdP.
Response
1{
2 "interactionHook": {
3 "url": "https://example-university.com/callback",
4 "claims": [],
5 "disabled": false,
6 "secret": "dGtUrijBOT6UUJ8JO4kAFyGfhahDlVVeIk/sPbWTa7c="
7 }
8}
You will get a shared secret
as a part of the response once you finish setting up the interactions hook - as seen above.
The secret should be used by your interaction hook to sign a new JWT with any additional claims that you may include. Libraries like Jose can be used for node servers.
Authorisation
Your interactions hook server/component is responsible for verifying your users, and it is exposed to the public internet, which means you must apply access controls to protect it from unauthorised access.
MATTR VII interactions hook integration supports a JWT (HS256)-based authorisation scheme to help enable this.
Get the shared secret
To sign the JWT, we need to use the secret
that is returned when you update your interaction hook.
Your interaction hook component will use this secret
to verify the JWT token that MATTR VII sends back.
Verify the session token
When MATTR VII redirects the end user to your interactions hook component, it will also include a JWT as a session_token
query parameter.
1https://example.com/mywebapp?session_token=GENERATED_JWT
This JWT contains key information such as redirectUrl
, which is used to redirect users back to the mobile wallet after they completed verification processes on the interaction hook component.
You should verify the JWT against the shared secret to make sure that the request is legitimate.
Here is an example code snippet using the panva/jose
library to verify the JWT:
1import { jwtVerify } from "jose";
2
3// Retrieved from URL session_token query parameter
4const jwt = "YOUR_JWT";
5
6// Recieved when you created an interaction hook on your tenant
7const secret = Buffer.from("SHARED_SECRET", "base64");
8
9const issuer = "https://YOUR_TENANT_URL";
10
11// The URL of your interation hook
12const audience = "https://example.com/mywebapp";
13
14const verifiedJwt = jwtVerify(
15 token,
16 secret,
17 { issuer, audience }
18);
The JWT is stored as a string in the jwt
variable.
The secret
variable contains the secret key that will be used to verify the JWT signature.
The issuer
and audience
variables are used to define the expected issuer and audience values for the JWT.
If the verification is successful, the function will return the decoded payload of the JWT. If the verification fails, an error will be thrown.
Extract the session token payload
After verifying the session token, your interactions hook component can extract several values from the decoded JWT payload.
Here is an example decoded payload with values that can be extracted:
1{
2 "state": "hJvfiSp3eEGybd-KmL8ja",
3 "scopes": ["ldp_vc:CourseCredential"],
4 "claims": { "email": "test@mattr.global" },
5 "authenticationProvider": {
6 "url": "https://myidentityprovider.auth0.com",
7 "subjectId": "user|123456789"
8 },
9 "redirectUrl": "https://YOUR_TENANT_URL/core/v1/oauth/interaction/hJvfiSp3eEGybd-KmL8ja/interactionhook/callback",
10 "sub": "a44a7f92-c61e-48a0-88b6-863eeeb58394",
11 "aud": "https://example.com/mywebapp",
12 "iss": "https://YOUR_TENANT_SUBDOMAIN.vii.mattr.global",
13 "iat": 1673910963,
14 "exp": 1673911263
15}
16
Here's what each of the values represents:
state
: A unique value associated with each interactions hook sessionscopes
: Scopes from end-user authorisation requestclaims
: User claims from Interactions Hook configurationauthenticationProvider
: A provider that the user is authenticated withurl
: URL of the authentication providersubjectId
: Subject Identifier of the end user for this provider
redirectUrl
: Url to redirect to when users complete interacting with the interactions hook componentsub
: Subject identifier of the end user for MATTR VIIaud
: Your interactions hook componentiss
: Your MATTR VII tenantiat
: Issued at (seconds since epoch)exp
: Expiry (seconds since epoch)
Response from interactions hook component
Once the end user has completed the custom journey, they need to be redirected back to MATTR VII to complete the credential issuance. The redirect URL can be found in the session token sent from MATTR VII to the interactions hook component above.
Additionally, the issuer must generate a new JWT session token and include it as a query parameter. This responding session token can be used to sync new claims with MATTR VII and used during issuance.
Example
1https://YOUR_TENANT_URL.vii.mattr.global/core/v1/oauth/interaction/hJvfiSp3eEGyb/interactionhook/callback?session_token=...
Generate a session token with claims
Your interactions hook component needs to sign the session token that is part of the redirect URL. This token can contain claims for the user as well.
To generate the session token with claims, use:
The same shared secret described in the authorisation section above.
The state value from the session token payload described in the authorisation section above.
Optionally, include new or updated claims you wish to merge with the existing end user claims on MATTR VII. These claims will be available for use during issuance.
1import { SignJWT } from "jose";
2
3const secret = Buffer.from(
4 "<SHARED_SECRET>",
5 "base64"
6);
7
8const jwt = await new SignJWT({
9 state: "hJvfiSp3eEGybd",
10 claims: { myNewClaim: "foobar" }
11 })
12 .setProtectedHeader({ alg: "HS256", typ: "JWT" })
13 .setIssuedAt()
14 .setExpirationTime("1m")
15 .sign(secret);
The setProtectedHeader
method is used to set the protected header of the JWT. In this case, the protected header includes the alg
algorithm (set to "HS256") and the type
token type (set to "JWT").
The setIssuedAt
method is used to set the "issued at" (iat
) claim to the current time. This indicates when the JWT was issued.
The setExpirationTime
method is used to set the expiration time (exp
) claim of the JWT. In this case, it is set to expire 1 minute after it is issued.
The resulting signed session token (JWT) will be used to transmit claims securely between systems.
Handling Errors
If your interaction hook determines that the user should not be allowed to continue with the issuance journey, you can return an error message in your signed JWT which will signal to the issuance journey that it should fail.
Here is an example of how to structure an error JWT:
1import { SignJWT } from "jose";
2
3const jwt = await new SignJWT({
4 state: "hJvfiSp3eEGybd-KmL8ja",
5 error: {
6 message: "Insufficient identity assurance level"
7 }
8})