4

I have a problem refreshing an AWS Cognito token using server side authentication in Go. I am able to get the id_token, access_token and refresh_token with the cognitoidentityprovider.AdminInitiateAuth method. I have create a User Pools Client with secrets so I have to provide the SECRET_HASH in the AuthParameters.

This all works fine when logging in, but the same secret hash doesn't work when refreshing the tokens. I have tripple checked the code and verified the secret hashes I send are the same when logging in and when refreshing the token (it should be the same since it uses the username, clientID and clientSecret which don't change).

The AWS API returns the following error:

{
"error": "NotAuthorizedException: Unable to verify secret hash for client myClientIdHere\n\tstatus code: 400, request id: c186ecf2-57a7-11e8-a01e-f97ed64650c9"
}

I have checked that device tracking is off as the documentation mentions that this is a problem when refreshing tokens on the server-side (note under "Admin authentication flow", https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html#amazon-cognito-user-pools-server-side-authentication-flow).

My refresh code is:

AWSRefreshToken := aws.String(refreshToken)
secretHash := secretHash(email, auth.Config.ClientID, auth.Config.ClientSecret)
AWSUserPoolID := aws.String(auth.Config.UserPoolID)

input := cognitoidentityprovider.AdminInitiateAuthInput{
    AuthFlow: aws.String("REFRESH_TOKEN_AUTH"),
    AuthParameters: map[string]*string{
        "REFRESH_TOKEN": AWSRefreshToken,
        "SECRET_HASH":   &secretHash,
    },
    ClientId:   &auth.Config.ClientID,
    UserPoolId: AWSUserPoolID,
}

output, err := auth.AWSCognitoIdentityProvider.AdminInitiateAuth(&input)

The secret hash code (from https://stackoverflow.com/a/46163403/3515197):

func secretHash(username, clientID, clientSecret string) string {
    mac := hmac.New(sha256.New, []byte(clientSecret))
    mac.Write([]byte(username + clientID))
    return base64.StdEncoding.EncodeToString(mac.Sum(nil))
}

I have checked other Stack Overflow questions but they only mention the device tracking problem and that the secret hash is needed. What am I missing here?

user3515197
  • 51
  • 1
  • 6
  • "Unable to verify secret hash for client myClientId", so you are sending 'myClientId' as your clientId. – RockOnGom Oct 17 '18 at 08:13

3 Answers3

11

Use user id (sub) instead of email while generating secret hash.

secretHash := secretHash(sub, auth.Config.ClientID, auth.Config.ClientSecret)
tikendra.chand
  • 371
  • 3
  • 7
  • 1
    This seemed to be the case for me. The original auth let me use the user's email in the secret but not for the refresh token. I can't find info in the documentation to support the *need* for the UUID from AWS in the SECRET_HASH and why it worked the first time without it. Can anyone provide a link to support this? – Geoff Jan 11 '20 at 00:10
  • This didn't seem to work for me, I instead tried `cognito:username` field and that worked for me – bencoder Jul 17 '20 at 08:41
2

I got the answer for this after smashing my head for a few hours. You gotta use the SUB as the username when getting the refresh token during the REFRESH_TOKEN_AUTH/REFRESH_TOKEN flow. I do not see this documented anywhere, and it's not returned when logging in via email as username, so it's super confusing. Do this: After you log a user in, do a GetUser call serverside with the newly minted accessToken to get that user's USERNAME which will be the value of that user's SUB in the cognito dashboard... WHY IS IT NOT JUST THE EMAIL ADDRESS!? GREAT QUESTION. Store the username (aka: the sub) in the user's cookie, so when they need the refresh token, you can use the sub as the username here. sheesh... this was super confusing.

Pandem1c
  • 778
  • 1
  • 5
  • 12
1

This is the case as well in Javascript. At work we have a Cognito User Pool with the Username attribute set to email, see Screenshot.

In my setup, some APIs can use the email to compute the Secret Hash but NOT initiateAuth(REFRESH_TOKEN_AUTH/REFRESH_TOKEN).

Here is a list of APIs that did work with the email as my username to compute:

  • initiateAuth(USER_PASSWORD_AUTH)
  • signUp
  • forgotPassword
  • confirmForgotPassword
  • confirmSignUp
  • resendConfirmationCode
mcdl
  • 11
  • 4