amplify-swift icon indicating copy to clipboard operation
amplify-swift copied to clipboard

API Auth (OIDC): Cognito cannot be used as issuer

Open dnys1 opened this issue 4 years ago • 7 comments

Describe the bug

Cognito Identity Tokens cannot be used with owner auth due to logic in AWSPluginsCore switching the "cognito:username" identity claim for "username".

Cognito ID tokens and Access Tokens have different structures. This logic in core seems to be accommodating changes to the latter; however, as a result, it seems to have also broken the former.

ID Token Payload

{
    "sub": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    "aud": "xxxxxxxxxxxxexample",
    "email_verified": true,
    "token_use": "id",
    "auth_time": 1500009400,
    "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_example",
    "cognito:username": "janedoe",
    "exp": 1500013000,
    "given_name": "Jane",
    "iat": 1500009400,
    "email": "[email protected]",
    "jti": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    "origin_jti": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
}

Access Token Payload

{
  "sub": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "device_key": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "cognito:groups": [
    "admin"
  ],
  "token_use": "access",
  "scope": "aws.cognito.signin.user.admin",
  "auth_time": 1562190524,
  "iss": "https://cognito-idp.us-west-2.amazonaws.com/us-west-2_example",
  "exp": 1562194124,
  "iat": 1562190524,
  "origin_jti": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "jti": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "client_id": "57cbishk4j24pabc1234567890",
  "username": "[email protected]"
}

Schema

type Todo 
  @model 
  @auth(rules: [
    { allow: owner, provider: oidc, identityClaim: "cognito:username" }
  ]) {
  id: ID!
  name: String!
  description: String
}

Steps To Reproduce

1. Use schema above
2. Follow steps here to use Cognito as OIDC provider: https://docs.amplify.aws/lib/graphqlapi/authz/q/platform/ios/#oidc
3. Add DataStore plugin
4. Observe failing subscriptions

Expected behavior

API plugin is able to pull "cognito:username" field from ID token when OIDC is being used as the API auth provider.

Amplify Framework Version

1.15.0 (likely applies to 1.13.0+, though)

Amplify Categories

API

Dependency manager

Cocoapods

Swift version

5.5

CLI version

6.3.0

Xcode version

13.0

Relevant log output

2021-10-13 09:20:59.756887-0700 DSSyncFix[42446:260476] [StarscreamAdapter] socket.write - {"id":"2720817C-B99F-4A04-8E0E-2F74B56EA3BB","payload":{"data":"{\"query\":\"subscription OnCreateTodo {\\n  onCreateTodo {\\n    id\\n    createdAt\\n    description\\n    name\\n    updatedAt\\n    __typename\\n    _version\\n    _deleted\\n    _lastChangedAt\\n    owner\\n  }\\n}\"}","extensions":{"authorization":{"Authorization":"eyJraWQiOiJQUzdjM2lNcWJudjV1bFlqdVNwdlE5dU12alRQZStPcld3V0pvZkN6a1p3PSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI5MWVlZmJjYi0xMWU2LTRlNzAtOWE4OS04YjhmN2NlNjgzMTciLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLXdlc3QtMi5hbWF6b25hd3MuY29tXC91cy13ZXN0LTJfR0xsUm5qWXhVIiwiY29nbml0bzp1c2VybmFtZSI6ImRpbGxvbiIsIm9yaWdpbl9qdGkiOiI3YWIxMzY5OC0xMzgyLTQxMjQtYTNlOC03MjBiMWQ1Njk1ZGIiLCJhdWQiOiIxZHBybWZzM3R1b25wbjJ0ZWRjcGQzaGNnNyIsImV2ZW50X2lkIjoiZDFjMjMwMTktZTZlYS00ODBlLTgwNWQtYWVkYTNkZWEwN2Y4IiwidG9rZW5fdXNlIjoiaWQiLCJhdXRoX3RpbWUiOjE2MzQxNDE4MDUsImV4cCI6MTYzNDE0NTQwNSwiaWF0IjoxNjM0MTQxODA1LCJqdGkiOiJjYjFjOGI5Mi1lZWI2LTQzZDAtODdhMS0wNmFlYWE5MTczZjUiLCJlbWFpbCI6Im55ZGlsbG9uQGFtYXpvbi5jb20ifQ.b-wUBdPLfyhUo06TqapnYfFkYL5UWC4P4otZ_co4BHHajG5TzsV07VWJYCum-7_pRWk3Mnu_iMTbRA2r1AcMgFaCvLx723d_0TmEdo4lILUM4y7AnL3wcSSO1tI5kKtQRnneGEkWMZ-BdXT-SlQg3DOSgF079HP5A6dpBj7waAeamUbXjblSK5ltMa8vApnDFPsO2gS_CJHg5JnkFKRIbxaz9rK-P4oCN_Qt_qjPsxrwwAe-pONLx8kE2iH8L8ZhyEUnC2Dfqug5IGII_zbC1Lmgt7lkRkEPEeF5LbRHBKAiV5si0POTpx7vTK3nHhhK_w_XMpTUGTXvKuYOKJIdZA","host":"zm3nltvjbjhotbjwze7ibdvd7a.appsync-api.us-west-2.amazonaws.com"}}},"type":"start"}
2021-10-13 09:20:59.852139-0700 DSSyncFix[42446:260480] [StarscreamAdapter] websocketDidReceiveMessage: - {"id":"2720817C-B99F-4A04-8E0E-2F74B56EA3BB","type":"error","payload":{"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument owner @ 'onCreateTodo'"}]}}

Is this a regression? (i.e. was this working before a version upgrade)

No response

Device

iPhone 13 Pro Max - Simulator

iOS Version

iOS 15

Specific to simulators

No response

Additional context

No response

dnys1 avatar Oct 13 '21 16:10 dnys1

From our discussion, one way to fix this is to check the provider from the authrule, if provider is oidc, do not take off the "cognito:" portion of the identity claim key. With this, we can update our integration test for OIDC: update the README.md to provision OIDC with Cognito, enable/refactor the disabled OIDC integ tests

lawmicha avatar Oct 14 '21 15:10 lawmicha

@lawmicha Not sure I understand this issue on a couple fronts. First, why are we using Cognito with OIDC settings? The standard Cognito User Pools auth does this behind the scenes anyway. Secondly, what is the purpose of the code which @dnys1 is referencing? We should be sending JWT tokens through to AppSync in the Authorization header unmodified so that AuthZ at the API level can take place followed by AuthZ at the Resolver level by looking at scopes can take place in the GraphQL request. Let's chat internally on this so we can come to a consistency resolution around implementation.

undefobj avatar Jun 06 '22 18:06 undefobj

Hi @undefobj, I recall this was testing different use cases that developers could be using, and one of them being using Cognito as the OIDC provider, i'll have to defer to @dnys1 to follow up on this

lawmicha avatar Jun 08 '22 15:06 lawmicha

Hi guys - yeah, sorry for the lack of context to this ticket. I had to rack my brain to remember why this was needed, but it came up again today with another user who wanted to use a custom Cognito claim for the identity claim. User Pool authorization will send the Cognito access token, which does not include these claims. By switching to OIDC, you can send the ID token, but because of the logic noted above, the Cognito ID token cannot be used even though it fits all the other criteria of an OIDC provider.

dnys1 avatar Jun 14 '22 18:06 dnys1

@undefobj the reason why the library has this code is for DataStore when establishing subscriptions, it will do two things- add the token to the request header and extract the identityClaim value from the token and add it as the owner input to the subscription operation.

@dnys1 I think you might have mentioned it back then when we originally discussed this, i thinnk the fix should be something like this:

// Current code - assumes when identityClaim from codegen is "cognito:username", then retrieves AccessToken.username
public func identityClaimOrDefault() -> String {
        guard let identityClaim = self.identityClaim else {
            return "username"
        }
        if identityClaim == "cognito:username" {
            return "username"
        }
        return identityClaim
    }

// Proposed code for the fix to work with OIDC providers
public func identityClaimOrDefault() -> String {
        guard let identityClaim = self.identityClaim else {
            return "username"
        }
        if identityClaim == "cognito:username" && provider == .userPools {
            return "username"
        }
        return identityClaim
    }

This way, when developer is using Cognito as the provider type, it will continue to return "username" to retrieve the value from the access token. When the provider is .odic then it will take the identityClaim as is which can be "cognito:username" or a custom field defined by the developer, from the ID token.

lawmicha avatar Jun 14 '22 19:06 lawmicha

Any news on this one? :)

diogoepsy avatar Feb 16 '24 09:02 diogoepsy

@diogoepsy Thank you - our team will investigate and post updates here. Could you share more details on the OIDC setup you're using. Also, if your schema and reproduction steps are different from the one mentioned above, it'd helpful to share it too.

thisisabhash avatar Mar 15 '24 18:03 thisisabhash