Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
1 task done
dnys1 opened this issue Oct 12, 2021 · 1 comment
Open
1 task done

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

dnys1 opened this issue Oct 12, 2021 · 1 comment
Labels
api bug Something isn't working GraphQL API Related to the API (GraphQL) category/plugins

Comments

@dnys1
Copy link
Contributor

dnys1 commented Oct 12, 2021

Before opening, please confirm:

Language and Async Model

Not applicable

Amplify Categories

GraphQL API, DataStore

Gradle script dependencies

implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.amplifyframework:core:1.28.0'
implementation "com.amplifyframework:aws-api:1.28.0"
implementation "com.amplifyframework:aws-api-appsync:1.28.0"

Environment information

# Put output below this line

------------------------------------------------------------
Gradle 6.7
------------------------------------------------------------

Build time:   2020-10-14 16:13:12 UTC
Revision:     312ba9e0f4f8a02d01854d1ed743b79ed996dfd3

Kotlin:       1.3.72
Groovy:       2.5.12
Ant:          Apache Ant(TM) version 1.10.8 compiled on May 10 2020
JVM:          11.0.12 (Amazon.com Inc. 11.0.12+7-LTS)
OS:           Mac OS X 11.6 x86_64

Please include any relevant guides or documentation you're referencing

https://docs.amplify.aws/lib/graphqlapi/authz/q/platform/android/#oidc

Describe the bug

Cognito Identity Tokens cannot be used with owner auth due to logic in core 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.

From Cognito docs:

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]"
}

Reproduction steps (if applicable)

  1. Use schema below
  2. Set primary auth as OIDC
$ amplify update api
? Please select from one of the below mentioned services: GraphQL
? Select from the options below Update auth settings
? Choose the default authorization type for the API OpenID Connect
? Enter a name for the OpenID Connect provider: Cognito
? Enter the OpenID Connect provider domain (Issuer URL): https://cognito-idp.<REGION>.amazonaws.com/<USER_POOL_ID>/
? Enter the Client Id from your OpenID Client Connect application (optional): 
? Enter the number of milliseconds a token is valid after being issued to a user: 3600000
? Enter the number of milliseconds a token is valid after being authenticated: 3600000
  1. Return Cognito ID token using OIDC auth provider in API category

Code Snippet

// Put your code below this line.

Log output

E/amplify:aws-datastore: Failure encountered while attempting to start API sync.
    DataStoreException{message=Error during subscription., cause=ApiAuthException{message=Attempted to subscribe to a model with owner-based authorization without username which was specified (or defaulted to) as the identity claim., cause=null, recoverySuggestion=If you did not specify a custom identityClaim in your schema, make sure you are logged in. If you did, check that the value you specified in your schema is present in the access key.}, recoverySuggestion=Evaluate details.}
        at com.amplifyframework.datastore.appsync.AppSyncClient.lambda$subscription$3(AppSyncClient.java:328)
        at com.amplifyframework.datastore.appsync.-$$Lambda$AppSyncClient$797ziDK0io-qXODzROLOA77stS8.accept(Unknown Source:4)
        at com.amplifyframework.api.aws.AWSApiPlugin.subscribe(AWSApiPlugin.java:317)
        at com.amplifyframework.api.aws.AWSApiPlugin.subscribe(AWSApiPlugin.java:288)
        at com.amplifyframework.api.ApiCategory.subscribe(ApiCategory.java:91)
        at com.amplifyframework.datastore.appsync.AppSyncClient.subscription(AppSyncClient.java:332)
        at com.amplifyframework.datastore.appsync.AppSyncClient.onUpdate(AppSyncClient.java:272)
        at com.amplifyframework.datastore.syncengine.-$$Lambda$r7L8lscweM53-6nW0zECJRGgjT0.subscribe(Unknown Source:7)
        at com.amplifyframework.datastore.syncengine.SubscriptionProcessor.lambda$subscriptionObservable$6$SubscriptionProcessor(SubscriptionProcessor.java:187)
        at com.amplifyframework.datastore.syncengine.-$$Lambda$SubscriptionProcessor$w6tohapLGUGmW4mOmsvNOno7GVE.subscribe(Unknown Source:11)
        at io.reactivex.rxjava3.internal.operators.observable.ObservableCreate.subscribeActual(ObservableCreate.java:40)
        at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13099)
        at io.reactivex.rxjava3.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42)
        at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13099)
        at io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
        at io.reactivex.rxjava3.core.Scheduler$DisposeTask.run(Scheduler.java:614)
        at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:65)
        at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:56)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)
     Caused by: ApiAuthException{message=Attempted to subscribe to a model with owner-based authorization without username which was specified (or defaulted to) as the identity claim., cause=null, recoverySuggestion=If you did not specify a custom identityClaim in your schema, make sure you are logged in. If you did, check that the value you specified in your schema is present in the access key.}
        at com.amplifyframework.api.aws.auth.AuthRuleRequestDecorator.getIdentityValue(AuthRuleRequestDecorator.java:157)
        at com.amplifyframework.api.aws.auth.AuthRuleRequestDecorator.decorate(AuthRuleRequestDecorator.java:123)
        at com.amplifyframework.api.aws.AWSApiPlugin.buildSubscriptionOperation(AWSApiPlugin.java:628)
        at com.amplifyframework.api.aws.AWSApiPlugin.subscribe(AWSApiPlugin.java:308)
        at com.amplifyframework.api.aws.AWSApiPlugin.subscribe(AWSApiPlugin.java:288) 
        at com.amplifyframework.api.ApiCategory.subscribe(ApiCategory.java:91) 
        at com.amplifyframework.datastore.appsync.AppSyncClient.subscription(AppSyncClient.java:332) 
        at com.amplifyframework.datastore.appsync.AppSyncClient.onUpdate(AppSyncClient.java:272) 
        at com.amplifyframework.datastore.syncengine.-$$Lambda$r7L8lscweM53-6nW0zECJRGgjT0.subscribe(Unknown Source:7) 
        at com.amplifyframework.datastore.syncengine.SubscriptionProcessor.lambda$subscriptionObservable$6$SubscriptionProcessor(SubscriptionProcessor.java:187) 
        at com.amplifyframework.datastore.syncengine.-$$Lambda$SubscriptionProcessor$w6tohapLGUGmW4mOmsvNOno7GVE.subscribe(Unknown Source:11) 
        at io.reactivex.rxjava3.internal.operators.observable.ObservableCreate.subscribeActual(ObservableCreate.java:40) 
        at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13099) 
        at io.reactivex.rxjava3.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42) 
        at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13099) 
        at io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96) 
        at io.reactivex.rxjava3.core.Scheduler$DisposeTask.run(Scheduler.java:614) 
        at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:65) 
        at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:56) 
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) 
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:923) 
W/amplify:aws-datastore: API sync failed - transitioning to LOCAL_ONLY.

amplifyconfiguration.json

const amplifyconfig = ''' {
    "UserAgent": "aws-amplify-cli/2.0",
    "Version": "1.0",
    "api": {
        "plugins": {
            "awsAPIPlugin": {
                "dsregression": {
                    "endpointType": "GraphQL",
                    "endpoint": "https://zm3nltvjbjhotbjwze7ibdvd7a.appsync-api.us-west-2.amazonaws.com/graphql",
                    "region": "us-west-2",
                    "authorizationType": "OPENID_CONNECT"
                }
            }
        }
    },
    "auth": {
        "plugins": {
            "awsCognitoAuthPlugin": {
                "UserAgent": "aws-amplify-cli/0.1.0",
                "Version": "0.1.0",
                "IdentityManager": {
                    "Default": {}
                },
                "AppSync": {
                    "Default": {
                        "ApiUrl": "https://zm3nltvjbjhotbjwze7ibdvd7a.appsync-api.us-west-2.amazonaws.com/graphql",
                        "Region": "us-west-2",
                        "AuthMode": "OPENID_CONNECT",
                        "ClientDatabasePrefix": "dsregression_OPENID_CONNECT"
                    }
                },
                "CredentialsProvider": {
                    "CognitoIdentity": {
                        "Default": {
                            "PoolId": "us-west-2:83f3a331-1419-40e2-a673-86dccd15f34f",
                            "Region": "us-west-2"
                        }
                    }
                },
                "CognitoUserPool": {
                    "Default": {
                        "PoolId": "us-west-2_GLlRnjYxU",
                        "AppClientId": "1dprmfs3tuonpn2tedcpd3hcg7",
                        "Region": "us-west-2"
                    }
                },
                "Auth": {
                    "Default": {
                        "authenticationFlowType": "USER_SRP_AUTH",
                        "loginMechanisms": [
                            "PREFERRED_USERNAME"
                        ],
                        "signupAttributes": [
                            "EMAIL"
                        ],
                        "passwordProtectionSettings": {
                            "passwordPolicyMinLength": 8,
                            "passwordPolicyCharacters": []
                        },
                        "mfaConfiguration": "OFF",
                        "mfaTypes": [
                            "SMS"
                        ],
                        "verificationMechanisms": [
                            "EMAIL"
                        ]
                    }
                }
            }
        }
    }
}''';

GraphQL Schema

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

Additional information and screenshots

No response

@rjuliano rjuliano added the GraphQL API Related to the API (GraphQL) category/plugins label Oct 14, 2021
@div5yesh div5yesh added the bug Something isn't working label Apr 19, 2022
@lawmicha lawmicha added the api label Sep 7, 2022
@lawmicha
Copy link
Contributor

lawmicha commented Feb 6, 2023

To allow backwards compat with older clients that were incorrectly generating "cognito:username", we can explicitly check for this case:

  1. If identityClaim is "cognito:username" and provider is userPool, return "username"
  2. Otherwise, return identityClaim.

In the default logic (2), should solve this issue, since the identityClaim is "cognito:username" and provider is ODIC should fall into this case.

Swift issue and proposed code change: aws-amplify/amplify-swift#1467 (comment)

@mattcreaser mattcreaser removed the p3 label Jul 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api bug Something isn't working GraphQL API Related to the API (GraphQL) category/plugins
Projects
None yet
Development

No branches or pull requests

6 participants