Passwordless OTP Login ( With nonce )

I am trying to integrate sms otp login experience for an application that we are building. The end goal is to create an onchain federated keyless account in aptos with the help of the id token that auth0 returns. The requirement on aptos side is that, the idtoken should have an nonce claim in the payload.

I am aware that if I utilize the /authorize request, basically universal login experience allows me passing nonce params. But I dont want to do that. We have dedicated mobile number input and otp input screen and we want to use that.

/paswordless/start and /oauth/token endpoints ( authentication apis ) can help me do that, but I could not find any way of passing custom nonce which i would expect in my id token payload.

Is there any way I can achieve this or is following Universal Login Flow the only solution ?

Our app is in react native btw.

Hi @chiratna

Welcome to the Auth0 Community!

If you are using the passwordless endpoint, you should be able to pass in the nonce in the authorization parameters of the request as such:

authParams: {
        nonce: nonce
      }

Once you have done that, you will be able to use a PostLogin Action to set the nonce as a custom claim inside the ID Token:

exports.onExecutePostLogin = async (event, api) => {
  // Check if the custom nonce exists in the transaction.
  const nonce = event.transaction.protocol_details?.authParams?.nonce;

  if (nonce) {
    // If the nonce exists, add it as a custom claim to the ID Token.
    const namespace = 'https://some_cool_namespace/claims';
    api.idToken.setCustomClaim(`${namespace}/nonce`, nonce);
    }
};

Let me know if the following works for you and if you have any other issues regarding the matter.

Kind Regards,
Nik

Hi Nik,
Thanks for replying back..
`event: { transaction: { protocol: 'oauth2-password', metadata: {} },`

It seems like i am not receiving any protocol_details payload in the postLogin event.

this is what my passwordless/start api payload looks like

{

“client_id”: “<client_id>”,

“client_secret”: “<client_secret>”, // For Regular Web Applications

“connection”: “sms”,

“phone_number”: “<phone_number>”,

“send”: “code”, //if left null defaults to link

“authParams”: { // any authentication parameters that you would like to add

“nonce”: “123456654321223455”

}

}

any reason why ? Am i missing anything ?

Thanks for the update!

What I am noticing in the provided information, it appears that the protocol you are using for the transaction is oauth2-password and not oidc-basic-profile. This would indicate that by the time that the transaction reaches the PostLogin Action, it is considered a new login process which does not include the initial authParams which you have provided to the /passwordless/start endpoint.

If you are making a POST to the /oauth/token endpoint, you might need to specify that you are using the oauth/grant-type/passwordless/otp grant type. To provide you more insight, a typical flow would be:

  • The initial POST to the /passwordless/start endpoint
{
  "client_id": "...",
  "connection": "sms",
  "phone_number": "...",
  "send": "code",
  "authParams": {
    "nonce": "123456654321223455"
  }
}
  • Post Request to the /oauth/token endpoint
{
    "grant_type": "http://auth0.com/oauth/grant-type/passwordless/otp", 
    "client_id": "...",
    "client_secret": "...",
    "username": "THE_PHONE_NUMBER",
    "otp": "THE_USER_ENTERED_CODE",
    "realm": "sms"
}

The authParams object from /passwordless/start is specifically tied to the passwordless authentication pipeline. When you exchange the OTP for tokens, you must use a grant_type that tells Auth0 to look for that pending passwordless transaction.

If you use a different grant_type , like password , Auth0 treats it as a completely new login attempt via the ROPG flow, which has no knowledge of the preceding /passwordless/start call or its authParams .

The most likely cause is that your /oauth/token request is using grant_type: 'password' instead of the correct grant type for this flow.

Please let me know if this fixes your issue. If not, could you share how exactly are you making these calls and what does the flow look like?

Kind Regards,
Nik

Hey Nik,
I really appreciate you taking out time, and helping me out with this. Apologies for the late reply here, I was off.
I am trying to onboard users to our app, using their mobile number. And as mentioned earlier, we would need to have custom screens for the login experience.

The first api call /passwordless/start with payload

{
  "client_id": "...",
  "client_secret": "....",
  "connection": "sms",
  "phone_number": "...", 
  "send": "code", 
  "authParams": { 
    "nonce": "123456654321223455"
  }
}

I can confirm i receive the otp in the supplied mobile number

the second api call i am doing is /oauth/token with payload

{
  "grant_type" : "http://auth0.com/oauth/grant-type/passwordless/otp",
  "client_id": "....",
  "client_secret": "....",
  "username":"...",
  "otp": "...",
  "realm": "sms", 
  "audience": "https://<domain>/api/v2/",
  "scope": "openid profile email"
}

The protocol of the transaction always comes out to be oauth2-password and not oidc-basic-profile , and the protocol_details metadata is missing.

Hi again @chiratna

It appears that there was a mistake I have made during the reproduction of the issue I was using the standard flow of Auth0 instead of the API flow completely overlooking the requirement of using your own custom UI.

Since the nonce claim is a a security feature of the OpenID Connect Implicit and Authorization Code flows meaning that it is not supported in the direct Resource Owner Password Grant (ROPG) or Passwordless API flows (in the case of the /oauth/token ) that you are using for your native UI. Also, any attempts to use api.idToken.setCustomClaim('nonce', ...) will be ignored or throw an error since it is expected to be passed through the normal /authorize flow.

If switching to Universal Login by using a web browser login approach and if Aptos allows the usage of custom claims, you can bass the nonce in the body of your /oauth/token call as such:

{
  "grant_type" : "http://auth0.com/oauth/grant-type/passwordless/otp",
  "client_id": "....",
  "client_secret": "....",
  "username":"...",
  "otp": "...",
  "realm": "sms", 
  "audience": "https://<domain>/api/v2/",
  "scope": "openid profile email",
  "my_nonce": "custom_nonce"
}

This can be set inside the ID or Access Tokens as such using a PostLogin Action trigger:

 if (event.request.body.my_nonce) {
    api.idToken.setCustomClaim('https://myapp.com/aptos_nonce', event.request.body.my_nonce);
  }

If I can help with anything else, let me know!

Kind Regards,
Nik