-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(apigateway): cognito user pool authorizer (#12786)
feat(apigateway): add support for cognito user pool authorizer closes #5618 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
- Loading branch information
1 parent
2aba609
commit ff1e5b3
Showing
7 changed files
with
438 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
115 changes: 115 additions & 0 deletions
115
packages/@aws-cdk/aws-apigateway/lib/authorizers/cognito.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import * as cognito from '@aws-cdk/aws-cognito'; | ||
import { Duration, Lazy, Names, Stack } from '@aws-cdk/core'; | ||
import { Construct } from 'constructs'; | ||
import { CfnAuthorizer } from '../apigateway.generated'; | ||
import { Authorizer, IAuthorizer } from '../authorizer'; | ||
import { AuthorizationType } from '../method'; | ||
import { IRestApi } from '../restapi'; | ||
|
||
/** | ||
* Properties for CognitoUserPoolsAuthorizer | ||
*/ | ||
export interface CognitoUserPoolsAuthorizerProps { | ||
/** | ||
* An optional human friendly name for the authorizer. Note that, this is not the primary identifier of the authorizer. | ||
* | ||
* @default - the unique construct ID | ||
*/ | ||
readonly authorizerName?: string; | ||
|
||
/** | ||
* The user pools to associate with this authorizer. | ||
*/ | ||
readonly cognitoUserPools: cognito.IUserPool[]; | ||
|
||
/** | ||
* How long APIGateway should cache the results. Max 1 hour. | ||
* Disable caching by setting this to 0. | ||
* | ||
* @default Duration.minutes(5) | ||
*/ | ||
readonly resultsCacheTtl?: Duration; | ||
|
||
/** | ||
* The request header mapping expression for the bearer token. This is typically passed as part of the header, in which case | ||
* this should be `method.request.header.Authorizer` where Authorizer is the header containing the bearer token. | ||
* @see https://docs.aws.amazon.com/apigateway/api-reference/link-relation/authorizer-create/#identitySource | ||
* @default `IdentitySource.header('Authorization')` | ||
*/ | ||
readonly identitySource?: string; | ||
} | ||
|
||
/** | ||
* Cognito user pools based custom authorizer | ||
* | ||
* @resource AWS::ApiGateway::Authorizer | ||
*/ | ||
export class CognitoUserPoolsAuthorizer extends Authorizer implements IAuthorizer { | ||
/** | ||
* The id of the authorizer. | ||
* @attribute | ||
*/ | ||
public readonly authorizerId: string; | ||
|
||
/** | ||
* The ARN of the authorizer to be used in permission policies, such as IAM and resource-based grants. | ||
* @attribute | ||
*/ | ||
public readonly authorizerArn: string; | ||
|
||
/** | ||
* The authorization type of this authorizer. | ||
*/ | ||
public readonly authorizationType?: AuthorizationType; | ||
|
||
private restApiId?: string; | ||
|
||
constructor(scope: Construct, id: string, props: CognitoUserPoolsAuthorizerProps) { | ||
super(scope, id); | ||
|
||
const restApiId = this.lazyRestApiId(); | ||
const resource = new CfnAuthorizer(this, 'Resource', { | ||
name: props.authorizerName ?? Names.uniqueId(this), | ||
restApiId, | ||
type: 'COGNITO_USER_POOLS', | ||
providerArns: props.cognitoUserPools.map(userPool => userPool.userPoolArn), | ||
authorizerResultTtlInSeconds: props.resultsCacheTtl?.toSeconds(), | ||
identitySource: props.identitySource || 'method.request.header.Authorization', | ||
}); | ||
|
||
this.authorizerId = resource.ref; | ||
this.authorizerArn = Stack.of(this).formatArn({ | ||
service: 'execute-api', | ||
resource: restApiId, | ||
resourceName: `authorizers/${this.authorizerId}`, | ||
}); | ||
this.authorizationType = AuthorizationType.COGNITO; | ||
} | ||
|
||
/** | ||
* Attaches this authorizer to a specific REST API. | ||
* @internal | ||
*/ | ||
public _attachToApi(restApi: IRestApi): void { | ||
if (this.restApiId && this.restApiId !== restApi.restApiId) { | ||
throw new Error('Cannot attach authorizer to two different rest APIs'); | ||
} | ||
|
||
this.restApiId = restApi.restApiId; | ||
} | ||
|
||
/** | ||
* Returns a token that resolves to the Rest Api Id at the time of synthesis. | ||
* Throws an error, during token resolution, if no RestApi is attached to this authorizer. | ||
*/ | ||
private lazyRestApiId() { | ||
return Lazy.string({ | ||
produce: () => { | ||
if (!this.restApiId) { | ||
throw new Error(`Authorizer (${this.node.path}) must be attached to a RestApi`); | ||
} | ||
return this.restApiId; | ||
}, | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from './lambda'; | ||
export * from './identity-source'; | ||
export * from './cognito'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
packages/@aws-cdk/aws-apigateway/test/authorizers/cognito.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import '@aws-cdk/assert/jest'; | ||
import * as cognito from '@aws-cdk/aws-cognito'; | ||
import { Duration, Stack } from '@aws-cdk/core'; | ||
import { AuthorizationType, CognitoUserPoolsAuthorizer, RestApi } from '../../lib'; | ||
|
||
describe('Cognito Authorizer', () => { | ||
test('default cognito authorizer', () => { | ||
// GIVEN | ||
const stack = new Stack(); | ||
const userPool = new cognito.UserPool(stack, 'UserPool'); | ||
|
||
// WHEN | ||
const authorizer = new CognitoUserPoolsAuthorizer(stack, 'myauthorizer', { | ||
cognitoUserPools: [userPool], | ||
}); | ||
|
||
const restApi = new RestApi(stack, 'myrestapi'); | ||
restApi.root.addMethod('ANY', undefined, { | ||
authorizer, | ||
authorizationType: AuthorizationType.COGNITO, | ||
}); | ||
|
||
// THEN | ||
expect(stack).toHaveResource('AWS::ApiGateway::Authorizer', { | ||
Type: 'COGNITO_USER_POOLS', | ||
RestApiId: stack.resolve(restApi.restApiId), | ||
IdentitySource: 'method.request.header.Authorization', | ||
ProviderARNs: [stack.resolve(userPool.userPoolArn)], | ||
}); | ||
|
||
expect(authorizer.authorizerArn.endsWith(`/authorizers/${authorizer.authorizerId}`)).toBeTruthy(); | ||
}); | ||
|
||
test('cognito authorizer with all parameters specified', () => { | ||
// GIVEN | ||
const stack = new Stack(); | ||
const userPool1 = new cognito.UserPool(stack, 'UserPool1'); | ||
const userPool2 = new cognito.UserPool(stack, 'UserPool2'); | ||
|
||
// WHEN | ||
const authorizer = new CognitoUserPoolsAuthorizer(stack, 'myauthorizer', { | ||
cognitoUserPools: [userPool1, userPool2], | ||
identitySource: 'method.request.header.whoami', | ||
authorizerName: 'myauthorizer', | ||
resultsCacheTtl: Duration.minutes(1), | ||
}); | ||
|
||
const restApi = new RestApi(stack, 'myrestapi'); | ||
restApi.root.addMethod('ANY', undefined, { | ||
authorizer, | ||
authorizationType: AuthorizationType.COGNITO, | ||
}); | ||
|
||
// THEN | ||
expect(stack).toHaveResource('AWS::ApiGateway::Authorizer', { | ||
Type: 'COGNITO_USER_POOLS', | ||
Name: 'myauthorizer', | ||
RestApiId: stack.resolve(restApi.restApiId), | ||
IdentitySource: 'method.request.header.whoami', | ||
AuthorizerResultTtlInSeconds: 60, | ||
ProviderARNs: [stack.resolve(userPool1.userPoolArn), stack.resolve(userPool2.userPoolArn)], | ||
}); | ||
|
||
expect(authorizer.authorizerArn.endsWith(`/authorizers/${authorizer.authorizerId}`)).toBeTruthy(); | ||
}); | ||
}); |
Oops, something went wrong.