0
votes

I have system composed of a mobile app and a backend (Django), the current auth workflow:

  • Mobile App signs in and receives a JWT using my authentication service
  • Mobile App sends JWT to REST API with every request

Now I want to allow mobile app to access aws resources (s3..etc) directly instead of going through the backend, so the desired workflow is:

https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_oidc_cognito.html

But instead of using amazon as the idP (as the image above) I want to create my own idP, and my questions are:

  • Is the JWT token from the current auth workflow becomes the identity-token or the access-token of the openid connect authorization code workflow?
  • How Cognito verify that the identity-token is issued from the same idP that is configured with (step 3)?
  • Or I Should separate the normal user authentication (current auth wokflow) from aws credentials auth workflow? how?
3

3 Answers

0
votes

Good question, though I would draw your diagram a little differently, in terms of an architecture to aim for, since there are quite a few concepts here that need separating:

enter image description here

KEY POINTS

  • Your app only talks to the Authorization Server (AS) (Cognito in your case) and only ever receives Cognito tokens. You have control over Cognito behaviour such as token claims and lifetimes.

  • Identity Providers are used for logins - these could be Google Sign In, SAML based or OIDC based. The AS takes care of issuing the same Cognito tokens to your app regardless of the IDP used for end user logins. My Federated Logins blog post has further info on this.

  • AWS Resources are external APIs and you have to supply them with the token they expect. This involves a form of token exchange, and in Cognito I believe this involves use of an Identity Pool.

  • I'd recommend doing token exchange via an API of your own since it will be cleaner from a security viewpoint, without exposing AWS resources such as DynamoDB directly to the internet. Also, if any secrets are involved in the token exchange, the API can send them securely whereas the mobile app cannot.

  • The industry standard is to only send access tokens to APIs and not id tokens. It is always possible that AWS breaks this rule, but send access tokens if you can.

  • If your app implements the recommended mobile flow OIDC via Authorization Code Flow (PKCE) then it will naturally have support for multiple logins. The other parts should then be relatively straightforward to layer on top. If it helps, my blog has a couple of sample mobile apps you can run, which both connect to Cognito in the is way.

0
votes

Partial answer on how cognito verify the id token, taken from here:

  1. The iss parameter must match the key used in the logins map (such as login.provider.com).
  2. The signature must be valid. The signature must be verifiable via an RSA public key.
  3. The fingerprint of the certificate hosting the public key matches what's configured on your OpenId Connect Provider.
  4. If the azp parameter is present, check this value against listed client IDs in your OpenId Connect provider.
  5. If the azp parameter is not present, check the aud parameter against listed client IDs in your OpenId Connect provider
0
votes

I had implemented Auth0 for my authentication service to receive the JWT Token and exchange the token to aws temporary credential. But I decided migrate my auth infra to Cognito User Pool to simplify my development.

Let me share the general concept what I did. I think it will be similar to your case:

  1. I had already auth0 client

  2. Obtaining the root CA thumbprint for an OpenID Connect Identity Provider. I created my own script to generate the cert thumbprint with NodeJS + Bash :

    // getCert.js
    
    var file=require("fs");
    var certs=file.readFileSync("./cert",'utf8');
    certs=certs.replace(/BEGIN CERTIFICATE-----\n/g,'BEGIN#####');
    certs=certs.replace(/\n-----END CERTIFICATE/g,'#####END');
    var allCerts=certs.match(/BEGIN#####([\s\S]*?)#####END/g);
    var lastCert=allCerts[allCerts.length-1].replace(/BEGIN#####/g,'-----BEGIN CERTIFICATE-----\n');
    lastCert=lastCert.replace(/#####END/g,'\n-----END CERTIFICATE-----');
    file.writeFileSync("./cert", lastCert, "utf8");
    
    # thumbprint.sh
    echo "Creating thumbprint certificate..."
    openssl s_client -showcerts -connect <AUTH0_CLIENT_DOMAIN>:<PORT> > cert &
    PID=$!
    wait $PID
    sleep 1
    node thumbprint/getCert.js
    preSHA="`openssl x509 -in cert -fingerprint -noout`"
    preSHA="${preSHA/SHA1 Fingerprint=/}"
    echo $preSHA | sed -e "s/://g" > cert
    
  3. I went to IAM Identity Providers and registered my Auth0 client as OpenID provider with the created cert thumbprint

  4. Created new identity pool with IAM Role Policy for both authenticated and unauthenticated roles

  5. On my identity pool, I setup the authentication provider to use OpenID Connect by put the created openid ARN and the Auth0 client domain

  6. There we go, we can test the token exchange by sample code below :

    let url = <auth0_client_domain>;
    let cognitoParam = {
      'IdentityPoolId': <the_id_of_identity_pool>,
      'Logins': {}
    };
    cognitoParam.Logins[url] = <jwt_idToken>;
    AWS.config.credentials=new AWS.CognitoIdentityCredentials(cognitoParam);
    let gp=AWS.config.credentials.getPromise();
    gp.then(()=>{
      console.log('getCredentials done: ',AWS.config.credentials.identityId)
    }).catch((err)=>{
      console.log("Error login : ", err.message);
    })