constructor()

in lib/base/oidc-client-stack.ts [36:200]


  constructor(scope: cdk.Construct, id: string, props: OidcClientStackProps) {
    super(scope, id);

    /** * API ** */

    const api = new apigateway.RestApi(this, 'oidc-client', {
      restApiName: 'oidc-client-api',
      description: 'This is mysaasapp client/backend headless mostly application that serves the purpose of vending warm cookies for signed-in users',
      deploy: false,
    });

    const table = dynamodb.Table.fromTableAttributes(this, 'TenantsTable', {
      tableName: props.tenantsTableName,
      globalIndexes: ['subdomain-index'],
    });

    /**
     * Authorizer function: Uses domain prefix, and path as lookup to retrieve tenant settings to be
     * used later in the mock context to stitch together the Cognito Auth URL.
     */

    const authFn = new nodejslambda.NodejsFunction(this, 'oidc-auth-function', {
      entry: `${path.join(path.resolve(__dirname, '..', '..'), 'resources', 'oidc-client', 'authorizer')}/index.js`,
      handler: 'authorizerHandler',
      timeout: cdk.Duration.seconds(900), // +acm validation wait of 530 seconds
      memorySize: 3008,
      environment: {
        TENANTS_TABLE_NAME: props.tenantsTableName,
      },
    });
    // Give Auth function read access to tenants table
    table.grantReadData(authFn);

    // Auth Lambda Policy to insert/read tenant specific secrets
    authFn.addToRolePolicy(new PolicyStatement({
      resources: [`arn:aws:secretsmanager:${this.region}:${this.account}:secret:/mysaasapp/*`],
      actions: ['secretsmanager:*Secret*'],
    }));

    const auth = new apigateway.RequestAuthorizer(this, 'oidc-authorizer', {
      handler: authFn,
      identitySources: ['method.request.header.Host'],
    });

    /**
     *
     * root -> [GET] redirect always to Auth endpoint
    */
    const getMockIntegration = new apigateway.MockIntegration({

      passthroughBehavior: apigateway.PassthroughBehavior.NEVER,
      requestTemplates: {
        'application/json': '{ "statusCode": 200 }',
      },
      integrationResponses: [{
        statusCode: '302',
        responseTemplates: { 'application/json': '#set($context.responseOverride.header.Location = $context.authorizer.auth_endpoint+"?client_id="+$context.authorizer.clientid+"&response_type="+$context.authorizer.response_type+"&scope="+$context.authorizer.scope+"&identity_provider="+$context.authorizer.idp_identifier+"&redirect_uri="+"https://"+$context.domainName+"/callback")' },
      }],

    });
    api.root.addMethod('GET', getMockIntegration, {
      authorizer: auth,
      requestParameters: { 'method.request.querystring.scope': true },
      methodResponses: [{ statusCode: '302', responseParameters: { 'method.response.header.Location': true } }],
    });

    /**
     *
     * callback [GET] code from OIDCP after Auth, redirects to getuserinfo
     *
    */

    const getCallback = api.root.addResource('callback');

    const getCallbackIntegration = new apigateway.MockIntegration({

      passthroughBehavior: apigateway.PassthroughBehavior.NEVER,
      requestTemplates: {
        'application/json': '{ "statusCode": 200 }',
      },

      integrationResponses: [{
        statusCode: '200',
        responseTemplates: { 'text/html': '#set($context.responseOverride.header.Content-Type = "text/html") <html><head><title>HTML from API Gateway/Lambda</title> <script type = "text/javascript">if((window.location.hash != "") || (window.location.href!= "")){let windowlocnew = window.location.toString(); windowlocnew = windowlocnew.replace("#","?"); windowlocnew = windowlocnew.replace("callback","userinfo");  window.location.replace(windowlocnew);}</script></head><body><h1>HTML from API Gateway/Lambda</h1></body></html>' },
      }],
    });
    getCallback.addMethod('GET', getCallbackIntegration, {
      methodResponses: [{ statusCode: '200' }],
    });

    /**
     * userinfo [GET] gets a warm cookie
     *
     */

    const clientFn = new nodejslambda.NodejsFunction(this, 'oidc-client-function', {
      entry: `${path.join(path.resolve(__dirname, '..', '..'), 'resources', 'oidc-client', 'oidc_client_function')}/handler.js`,
      handler: 'hello',
      timeout: cdk.Duration.seconds(900),
      memorySize: 3008,
      environment: {
        resource_endpoint: props.oidcResourceUrl,
        TENANTS_TABLE_NAME: props.tenantsTableName,
      },
    });
    const getUserInfo = api.root.addResource('userinfo');

    const getUserInfoIntegration = new apigateway.LambdaIntegration(clientFn);

    getUserInfo.addMethod('GET', getUserInfoIntegration, { authorizer: auth }); // GET /

    // Give client function read access to tenants table
    table.grantReadData(clientFn);

    /**
     *
     * admin app
     */
    const getAdminMockIntegration = new apigateway.MockIntegration({

      passthroughBehavior: apigateway.PassthroughBehavior.NEVER,
      requestTemplates: {
        'application/json': '{ "statusCode": 200 }',
      },
      integrationResponses: [{
        statusCode: '302',
        responseTemplates: { 'application/json': '#set($context.responseOverride.header.Location = $context.authorizer.auth_endpoint+"?client_id="+$context.authorizer.clientid+"&response_type="+$context.authorizer.response_type+"&scope="+$context.authorizer.scope+"&identity_provider=COGNITO&redirect_uri="+"https://"+$context.domainName+"/callback")' },
      }],

    });
    const getAdminResource = api.root.addResource('admin');
    getAdminResource.addMethod('GET', getAdminMockIntegration, {
      authorizer: auth,
      requestParameters: { 'method.request.querystring.scope': true },
      methodResponses: [{ statusCode: '302', responseParameters: { 'method.response.header.Location': true } }],
    });

    const deployment = new apigateway.Deployment(this, 'Deployment', { api });

    const stage = new apigateway.Stage(this, 'dev', {
      stageName: 'dev',
      deployment,
      loggingLevel: apigateway.MethodLoggingLevel.INFO,
      dataTraceEnabled: true,
    });

    api.deploymentStage = stage;

    new ssm.StringParameter(this, 'oidcClientEndPoint', {
      parameterName: '/mysaasapp/oidcClientEndPoint',
      stringValue: api.url,
    });

    new ssm.StringParameter(this, 'oidcClientCallBackEndPoint', {
      parameterName: '/mysaasapp/oidcClientCallBackEndPoint',
      stringValue: `${api.url}/callback`,
    });
    new ssm.StringParameter(this, 'oidcClientRestApiId', {
      parameterName: '/mysaasapp/oidcClientRestApiId',
      stringValue: api.restApiId,
    });

    this.clientCallbackUrl = `${api.url}/callback`;
    this.clientUrl = api.url;
  }