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

Missing Feature - UserPool client configuration (Callback URLs and scopes) #3037

Closed
1 of 5 tasks
CSmither opened this issue Jun 24, 2019 · 19 comments
Closed
1 of 5 tasks
Assignees
Labels
@aws-cdk/aws-cognito Related to Amazon Cognito effort/medium Medium work item – several days of effort feature-request A feature should be added or improved.

Comments

@CSmither
Copy link

Ability to configure a Cognito User Pool Clients including callback URLs, signout URLs, and allowed scopes.

  • I'm submitting a ...

    • 🪲 bug report
    • 🚀 feature request
    • 📚 construct library gap
    • ☎️ security issue or vulnerability => Please see policy
    • ❓ support request => Please see note at the top of this template.
  • What is the current behavior?
    Can create UserPools and UserPool clients, however the configuration of the client is very limited. Can only specify clientName, enabledAuthFlows, and whether to create a secret or not.

  • What is the expected behavior (or behavior of feature suggested)?
    Would also like to be able to configure callback URLs, signout URLs, and allowed scopes so a complete functional UserPool can be created from scratch.

  • What is the motivation / use case for changing the behavior or adding this feature?
    Ability to create an ApiGateway secured by a cognito userpool with specific callback URLs and available scopes.

  • Please tell us about your environment:

    • CDK CLI Version: 0.34.0
    • OS: Windows 10 | Debian Stretch
    • Language: TypeScript
  • Other information (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. associated pull-request, stackoverflow, gitter, etc)
    Currently I am creating the userpool like below, I believe this to be correct but please tell me if this is the wrong way to go about this?

    const userPool = new cognito.UserPool(this, "userPool", {
      autoVerifiedAttributes: [],
      poolName: "userPool",
      signInType: cognito.SignInType.Username,
      usernameAliasAttributes: [
        cognito.UserPoolAttribute.Email,
        cognito.UserPoolAttribute.PhoneNumber
      ]
    });

    const userPoolClient = new cognito.UserPoolClient(
      this,
      "userPoolClient",
      {
        clientName: "Client",
        enabledAuthFlows: [
          cognito.AuthFlow.AdminNoSrp,
          cognito.AuthFlow.CustomFlowOnly
        ],
        userPool: userPool
      }
    );
@NGL321 NGL321 added needs-triage This issue or PR still needs to be triaged. @aws-cdk/aws-cognito Related to Amazon Cognito labels Jun 24, 2019
@NGL321
Copy link
Contributor

NGL321 commented Jun 24, 2019

Hi Callum, thanks for reaching out!

Unfortunately, it appears that CloudFormation doesn't currently support setting callback or signout URLs.
I recommend commenting here to try to bring some attention to the issue. Meanwhile I have put in an internal ticket to the Cloudformation team regarding this gap.

Once they have resolved the issue, I will reopen this issue as a feature-request.

@NGL321 NGL321 added feature-request A feature should be added or improved. gap closing-soon This issue will automatically close in 4 days unless further comments are made. and removed needs-triage This issue or PR still needs to be triaged. labels Jun 24, 2019
@CSmither
Copy link
Author

Thanks will close for now and keep an eye on cloud formation updates.

@NGL321 NGL321 removed the closing-soon This issue will automatically close in 4 days unless further comments are made. label Jun 26, 2019
@NGL321
Copy link
Contributor

NGL321 commented Jul 15, 2019

In an effort to have better visibility of Cfn support gaps, we are going to keep these open, so I am going to reopen this issue.

@NGL321 NGL321 reopened this Jul 15, 2019
@eladb eladb self-assigned this Aug 12, 2019
@NGL321 NGL321 added needs-cfn This issue is waiting on changes to CloudFormation before it can be addressed. and removed no-cfn-supp labels Aug 23, 2019
@dveijck
Copy link

dveijck commented Sep 26, 2019

From the CloudFormation documentation on UserPoolClient I would expect CloudFormation to support callback and logout URLS. Not sure if this was added recently by the CloudFormation team, but I thought it would be useful to post it here.

So this means that for now you could use the Cfn escape hatch as long as not all features in the CDK classes are implemented. Something like this gives you an idea:

const userPool = new UserPool(...);

...

const userPoolClient = new UserPoolClient(
    scope,
    'user-pool-client',
    {
      userPoolClientName: 'user-pool-client-name',
      userPool: userPool,
      enabledAuthFlows: [AuthFlow.USER_PASSWORD],
      generateSecret: true
    }
);
const cfnUserPoolClient = userPoolClient.node.defaultChild as CfnUserPoolClient;
cfnUserPoolClient.supportedIdentityProviders = ['COGNITO'];
cfnUserPoolClient.callbackUrLs = ['https://your_service_domain/oauth2/idpresponse'];
cfnUserPoolClient.allowedOAuthFlowsUserPoolClient = true;
cfnUserPoolClient.allowedOAuthFlows = ['code'];
cfnUserPoolClient.allowedOAuthScopes = ['openid'];

Be aware of the 'strange' capitalizing of the callbackUrLs and logoutUrLs methods.

I did give it a run with CDK 1.9.0 and it seemed to create the UserPoolClient with the callback URL just fine for me.

@eladb eladb assigned nija-at and unassigned eladb Jan 23, 2020
@nija-at nija-at removed the needs-cfn This issue is waiting on changes to CloudFormation before it can be addressed. label Jan 31, 2020
@nija-at nija-at added effort/medium Medium work item – several days of effort and removed gap labels Feb 25, 2020
@misterjoshua
Copy link
Contributor

misterjoshua commented Feb 28, 2020

I'm not sure if this is the right place, but in my use case, I'd like to authenticate with cognito from an application load balancer action using a secret generated via a UserPoolClient or CfnUserPoolClient.

It doesn't seem clear how the oidc client secret can be gotten from the UserPoolClient and given to the application load balancer rule actions, as I seem to get a nonsense value from from the UserPoolClient.userPoolClientClientSecret property.

Apparently there was a ClientSecret attribute documented on UserPoolClient resources at one point. I'm not sure what happened.

awsdocs/aws-cloudformation-user-guide#72

@dveijck
Copy link

dveijck commented Feb 28, 2020

Hi @misterjoshua,

I believe there is no convenient way to automate this at the moment. The client secret is not available through CloudFormation and thus there is no way to use it from CDK simply by referencing the attribute.

That said, what you might be able to do is the following*:

  • Within the UserPoolClient stack create a CloudFormation custom resource backed by an AWS Lambda.
  • The Lambda function (with the appropriate roles) could be used to get the client secret
  • Then create a parameter in either SSM or SecretsManager.
  • As SSM and SecretsManager secret parameters are supported as input by CloudFormation/CDK you might be able to reference those within your ALB stack and use that in your ALB rule actions.

*Not sure if this plays well if all resources are defined within the same stack, because secrets needs to be available at the moment the CloudFormation template is being rolled out. But if you use different stacks for your Cognito UserPoolClient and ALB resources I don't see why it wouldn't work.

I haven't tried this myself, so that's how far my warranty goes ;-), but it might be worth a try.

By the way have you considered posting your question on Stackoverflow? Maybe other people might have a potential solution for this as well.

@0xdevalias
Copy link
Contributor

May be worth checking out and subscribing to aws/aws-cdk-rfcs#95 as well.

@ran-isenberg
Copy link

I'm having an issue with something similar. I'm unable to use the suggested SSM parameter if the string is secured.
See #6819

@0xdevalias
Copy link
Contributor

0xdevalias commented Mar 19, 2020

As a followup to @dveijck's post above replying to @misterjoshua; CDK has a really short/convenient syntax for custom resources that just need to call AWS SDK functions:

A basic example (untested for this use case exactly) derived from some similar code I wrote recently:

const describeCognitoUserPoolClient = new cr.AwsCustomResource(
      this,
      'DescribeCognitoUserPoolClient',
      {
        resourceType: 'Custom::DescribeCognitoUserPoolClient',
        onCreate: {
          region: 'us-east-1',
          service: 'CognitoIdentityServiceProvider',
          action: 'describeUserPoolClient',
          parameters: {
            UserPoolId: userPool.userPoolId,
            ClientId: userPoolClient.userPoolClientId,
          },
          physicalResourceId: cr.PhysicalResourceId.of(userPoolClient.userPoolClientId),
        },
        // TODO: can we restrict this policy more?
        policy: cr.AwsCustomResourcePolicy.fromSdkCalls({
          resources: cr.AwsCustomResourcePolicy.ANY_RESOURCE,
        }),
      }
    )

    const userPoolClientSecret = describeCognitoUserPoolClient.getResponseField(
      'UserPoolClient.ClientSecret'
    )
    new cdk.CfnOutput(this, 'UserPoolClientSecret', {
      value: userPoolClientSecret,
    })

@nija-at
Copy link
Contributor

nija-at commented Apr 7, 2020

The original issue opened here has been resolved - 09852d0

I've opened a separate issue to track retrieval of the client secret - #7225. Please +1 and comment over there.

Thanks!

@nija-at nija-at closed this as completed Apr 7, 2020
@huankimtran
Copy link

Hello,
I am wondering if the Sign out URL has been implemented yet? I can only find the Callback Url

Thanks

@leantorres73
Copy link

I'm wondering the same, I cannot find the sign out URL as params. I'm using cdk in typescript.

@katiewoolston
Copy link

katiewoolston commented Aug 10, 2020

@huankimtran @leantorres73 the parameter is called logoutUrLs.

@leantorres73
Copy link

@katiewoolston maybe it exists for other languages, for typescript is not implemented.

export interface OAuthSettings {
    /**
     * OAuth flows that are allowed with this client.
     * @see - the 'Allowed OAuth Flows' section at https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-app-idp-settings.html
     * @default {authorizationCodeGrant:true,implicitCodeGrant:true}
     */
    readonly flows?: OAuthFlows;
    /**
     * List of allowed redirect URLs for the identity providers.
     * @default - ['https://example.com'] if either authorizationCodeGrant or implicitCodeGrant flows are enabled, no callback URLs otherwise.
     */
    readonly callbackUrls?: string[];
    /**
     * OAuth scopes that are allowed with this client.
     * @see https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-app-idp-settings.html
     * @default [OAuthScope.PHONE,OAuthScope.EMAIL,OAuthScope.OPENID,OAuthScope.PROFILE,OAuthScope.COGNITO_ADMIN]
     */
    readonly scopes?: OAuthScope[];
}

@katiewoolston
Copy link

katiewoolston commented Aug 17, 2020

@leantorres73 I'm using TypeScript, but the logoutUrLs attribute is in the user pool App Client constructor props. This is how I set it:

import { CfnUserPoolClient } from '@aws-cdk/aws-cognito';

const userPoolClient = new CfnUserPoolClient(this, 'MyAppClient', {
  clientName: 'MyAppClient',
  userPoolId: 'some-userpool-id',
  ...someOtherParams,
  callbackUrLs: [myAppUrl],
  logoutUrLs: [myAppUrl],
});

@leantorres73
Copy link

@katiewoolston got it, I'm using the pool.addClient() method, which has the callbackUrls but it doesn't have the logoutUrls. This was my example:

     const client = pool.addClient('app-client', {
      oAuth: {
        flows: {
          authorizationCodeGrant: true,
        },
        scopes: [ OAuthScope.OPENID, OAuthScope.EMAIL, OAuthScope.PROFILE ],
        callbackUrls: [ '', '' ]
      }
    });

@katiewoolston
Copy link

Ah, I see - yeah, it doesn't look like you can do it that way.

@kuzne4ik537
Copy link

kuzne4ik537 commented Mar 4, 2021

@0xdevalias

As a followup to @dveijck's post above replying to @misterjoshua; CDK has a really short/convenient syntax for custom resources that just need to call AWS SDK functions:

A basic example (untested for this use case exactly) derived from some similar code I wrote recently:

const describeCognitoUserPoolClient = new cr.AwsCustomResource(
      this,
      'DescribeCognitoUserPoolClient',
      {
        resourceType: 'Custom::DescribeCognitoUserPoolClient',
        onCreate: {
          region: 'us-east-1',
          service: 'CognitoIdentityServiceProvider',
          action: 'describeUserPoolClient',
          parameters: {
            UserPoolId: userPool.userPoolId,
            ClientId: userPoolClient.userPoolClientId,
          },
          physicalResourceId: cr.PhysicalResourceId.of(userPoolClient.userPoolClientId),
        },
        // TODO: can we restrict this policy more?
        policy: cr.AwsCustomResourcePolicy.fromSdkCalls({
          resources: cr.AwsCustomResourcePolicy.ANY_RESOURCE,
        }),
      }
    )

    const userPoolClientSecret = describeCognitoUserPoolClient.getResponseField(
      'UserPoolClient.ClientSecret'
    )
    new cdk.CfnOutput(this, 'UserPoolClientSecret', {
      value: userPoolClientSecret,
    })

Hey. When I'm trying to deploy it with cdk codePipeline I have this issue:
Screen Shot 2021-03-04 at 6 32 36 PM
Did it work for You without this issue?

@steven87vt
Copy link

steven87vt commented Nov 29, 2021

@katiewoolston got it, I'm using the pool.addClient() method, which has the callbackUrls but it doesn't have the logoutUrls. This was my example:

My issue is I need the client id to construct the default login/logout urls for simple route protection using default cognito signing page. You cannot modify the oath properties without layer 1 resources which I want to avoid. The logout url and login url could really just have a boolean option to assume the defaults or accept callbacks which return the string[] type.

this.userPool = new UserPool(this, 'user-pool', {
  someOptions
  ...
})

this.userPoolDomain = this.userPool.addDomain('user-pool-domain', {
  someOptions
  ...
})

const baseUrl = this.userPoolDomain.baseUrl()
// const clientId = this.userPoolClient.userPoolClientId // obviously throws undefined
const loginRedirect = `${baseUrl}/oauth2/idpresponse`

const oAuth: OAuthSettings = {
  scopes: [OAuthScope.EMAIL, OAuthScope.PHONE, OAuthScope.PROFILE, OAuthScope.OPENID],
    flows: {
      authorizationCodeGrant: true
    },
   callbackUrls: [loginRedirect],
  // logoutUrls: [] // cannot be constructed without this.userPoolClient.userPoolClientId
  // https://myurl/logout?client_id=${unknown_by_this_point}&redirect_uri=${loginRedirect}&response_type=code&scope=openid
 }

this.userPoolClient = new UserPoolClient(this, 'user-pool-client', {
  userPool: this.userPool,
  someOptions
  ..
  oAuth
})

// this.userPoolClient // no mutations available for callback or logout urls.

I eventually tried the CfnUserPoolClient solution as referenced above but in my case it introduced a circular dependency. My user pool client needs its id to create itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-cognito Related to Amazon Cognito effort/medium Medium work item – several days of effort feature-request A feature should be added or improved.
Projects
None yet
Development

No branches or pull requests