skip to Main Content

I want to authenticate users using Cognito, with option to use Facebook. User can sign_in/sign_up using either of those options.

I have created Cognito User Pool and Cognito Federated Identity, and also I have created Facebook App for authentication. Both User Pool and Facebook app are connected to Federated identity.

When I sign_up and later authenticate Cognito User via Cognito User Pool, then Cognito returns accessToken, which I store in localStorage on front and use whenever needed for athentication.

I have /authenticate endpoint (express), that takes in username & password, and returns accessToken if all went well. Whenever I make API call that requires auth, I send accessToken that I have in local storage. It goes, more or less as this:

// POST user/authenticate
const authenticationData = {
  Username: username,
  Password: password
}

authenticationDetails = new AuthenticationDetails(authenticationData)

const userData = {
  Username: username,
  Pool: userPool()
}
cognitoUser = new CognitoUser(userData)

cognitoUser.authenticateUser(authenticationDetails, {
  onSuccess: (res) => resolve(res), // here I get accessToken
  onFailure: (err) => {
    console.log('[authenticateUser error]', err)
    reject(err)
  },
//...

However

When I use Facebook, I do not get accessToken I could use in same fashion. I get accessToken from Facebook via FB.login, I pass it to Cognito to authenticate, and then I don’t know what to do, because I cannot get any token that could be used to authenticate API calls, that require Cognito Authentication.

Here’s what I do:

await window.FB.login((response) => { 
  props.userFacebookSignIn(response)
})
// ...

call(foo, 'users/facebook_sign_in', { accessToken: payload.facebookAccessToken })
// ...

// users/facebook_sign_in
AWS.config.region = config.AWSRegion
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
  IdentityPoolId: 'foo',
  Logins: {
    'graph.facebook.com': facebookAccessToken
  }
})

AWS.config.credentials.get((err) => {
  // Here I get no errors, I presume that I have logged Facebook user in 
  const accessKeyId = AWS.config.credentials.accessKeyId
  const secretAccessKey = AWS.config.credentials.secretAccessKey
  const sessionToken = AWS.config.credentials.sessionToken
  // here I can do stuff probably, 
  // but I would like to receive token that would allow me to do stuff, 
  // rather than context I can do stuff in
})

While I am doing all of this, I have this feeling, that devs at AWS implemented Cognito as frontend solution, rather than something to be used in backend. Correct me if I am wrong.

Nevertheless, I would like to be able authenticate api calls using Cognito and Facebook interchangeably in express middleware.

Is that possible? Thanks.

2

Answers


  1. Chosen as BEST ANSWER

    I decided to use oAuth.

    Here's quick & dirty look on how it's done

    In AWS Cognito

    1) Set up Cognito User Pool. Add App Client save App client id & App client secret as COGNITO_CLIENT_ID and COGNITO_CLIENT_SECRET

    2) Go to Federation > Identity providers and add your Facebook app ID and App secret (both you will find in Facebook app panel)

    3) Go to App integration > App client settings click "Select all", set up your Callback URL, mine is localhost:5000/facebook also select Authorization code grant and Allowed OAuth Scopes (save scopes to say: COGNITO_SCOPES)

    4) Now go to App integration > Domain name and enter your custom domain; let's say example-app-debug so it's: https://example-app-debug.auth.us-east-1.amazoncognito.com

    That's all there is to Cognito

    no the Facebook part

    5) Settings > Basic add example-app-debug.auth.us-east-1.amazoncognito.com to your App domains - Save Changes

    6) In Facebook Login > Settings in Valid OAuth Redirect URIs add this URL: https://example-app-debug.auth.us-east-1.amazoncognito.com/oauth2/idpresponse and Save Changes

    and the code

    In browser, redirect user to this url when Login w. Facebook button is clicked:

    window.location.href =
      `https://example-app-debug.auth.us-east-1.amazoncognito.com/oauth2/authorize` +
        `?identity_provider=Facebook` +
        `&redirect_uri=http://localhost:5000/facebook` +
        `&response_type=code` +
        `&client_id=${COGNITO_CLIENT_ID}` +
        `&scope=${COGNITO_SCOPES}`
    

    this call should come back to you with a code, like this: http://localhost:5000/facebook?code=foo-bar-code Send this code to your backend.

    In backend, do this:

    const axios = require('axios')
    const url = `` +
    `https://${COGNITO_CLIENT_ID}:${COGNITO_CLIENT_SECRET}` + 
    `@example-app-debug.auth.us-east-1.amazoncognito.com/oauth2/token` +
    `?grant_type=authorization_code` + 
    `&code=foo-bar-code` + // <- code that came from Facebook
    `&redirect_uri=http://localhost:5000/facebook` +
    `&client_id=${COGNITO_CLIENT_ID}`
    
    const response = await axios.post(url) 
    // response should have access_token, refresh_token and id_token in data
    

    You send access_token, refresh_token and id_token back to frontend and save them in local storage and use them to authenticate and Done.


  2. I have used federated identity for salesforce single sign on but i imagine the steps will the same. After authenticating with facebook you will recieve and id_token from them in response. You have to pass this as a parameter in the getId method:

    var params = {
      IdentityPoolId: 'STRING_VALUE', /* required */
      AccountId: 'STRING_VALUE',
      Logins: {
        '<IdentityProviderName>': 'STRING_VALUE',
        /* 'graph.facebook.com': ... */
      }
    };
    cognitoidentity.getId(params, function(err, data) {
      if (err) console.log(err, err.stack); // an error occurred
      else     console.log(data);           // successful response
    });
    

    In the result you will get an identity id which you can save somewhere so that you don’t have to make this call everytime while authenticating. Now take this identity id and make the getCredentialsForIdentity call:

    response = client.get_credentials_for_identity(
        IdentityId='string',
        Logins={
            'string': 'string'
        },
        CustomRoleArn='string'
    )
    

    This will finally give you the temporary access key, secret key and session key you need.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search