Skip to content

Build your AWS API Gateway custom authorizer lambda without the need to handle tokens by yourself. Just implement the logic...

License

Notifications You must be signed in to change notification settings

nordcloud/cognito-authorizer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build Status Go Report Card

Cognito authorizer

A golang packages that abstract out work with JSON web access/identity tokens for AWS API Gateway custom authorizer.

These packages handle:

  • access, id and standard tokens
  • token verification
  • token payload decrypting (claims)
  • building proper responses from a custom authorizer
  • a M2M token signer helper

You don't need to worry about JWT. The GetIDClaims, GetAccessClaims and GetStandardClaims will do the work for you, so you can focus only on building APIGatewayCustomAuthorizerPolicy.

Docs

About resource server context

You can pass a context created by your custom authorizer to the resource server. This is done by satisfying ContextBuilder interface. The method should return a map[string]interface{} (this is how AWS golang SDK works) but keys and values in this map have to be strings. More info here.

Example

Custom authorizer main package

package main

import (
	"context"
	"fmt"
	"os"
	"strings"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
	log "github.com/sirupsen/logrus"

	cognitoAuthorizer "github.com/nordcloud/cognito-authorizer/pkg/authorizer"
)

type PolicyEffect string

const (
	allow PolicyEffect = "allow"
	deny  PolicyEffect = "deny"
)

type Policy struct {
	Context *cognitoAuthorizer.Context
}

func (b Policy) BuildPolicy(encodedToken string) (events.APIGatewayCustomAuthorizerPolicy, error) {
	accessClaims := &cognitoAuthorizer.AccessTokenClaims{}
	err := cognitoAuthorizer.GetAccessClaims(encodedToken, b.Context.DecryptionKeys, accessClaims)
	if err != nil {
		return events.APIGatewayCustomAuthorizerPolicy{}, err
	}

	resources := []string{
		fmt.Sprintf(
			"arn:aws:execute-api:%s:*:%s/%s/*/*",
			b.Context.Region,
			b.Context.ApplicationID,
			b.Context.Stage,
		),
	}

	policy := events.APIGatewayCustomAuthorizerPolicy{
		Version: "2012-10-17",
		Statement: []events.IAMPolicyStatement{
			{
				Action:   []string{"execute-api:Invoke"},
				Effect:   string(allow),
				Resource: resources,
			},
		},
	}

	return policy, nil
}

func (b Policy) BuildContext(encodedToken string) (map[string]interface{}, error) {
	accessClaims := &cognitoAuthorizer.AccessTokenClaims{}
	err := cognitoAuthorizer.GetAccessClaims(encodedToken, b.Context.DecryptionKeys, accessClaims)
	if err != nil {
		return map[string]interface{}{}, err
	}

	context := map[string]interface{}{
		"token_scope": accessClaims.Scope,
	}

	return context, nil
}

var (
	sharedContext *cognitoAuthorizer.Context
)

// Init is called on lambda cold start. In this function we pull Cognito keys to verify tokens.
func init() {
	sharedContext = &cognitoAuthorizer.Context{
		Region:            os.Getenv("REGION"),
		ApplicationID:     os.Getenv("API_APPLICATION_ID"),
		Stage:             os.Getenv("API_STAGE"),
		AllowedUserPoolID: os.Getenv("API_ALLOWED_USER_POOL_ID"),
		CognitoClients:    strings.Split(os.Getenv("COGNITO_CLIENTS"), ","),
		DecryptionKeys:    nil,
	}

	keys, err := cognitoAuthorizer.GetDecryptionKeys(sharedContext.Region, sharedContext.AllowedUserPoolID)
	if err != nil {
		log.WithField("error", err).Error("Unable to get decryption keys.")
	}

	sharedContext.DecryptionKeys = keys

	log.WithFields(log.Fields{
		"region":               sharedContext.Region,
		"application_id":       sharedContext.ApplicationID,
		"stage":                sharedContext.Stage,
		"allowed_user_pool_id": sharedContext.AllowedUserPoolID,
		"cognito_clients":      sharedContext.CognitoClients,
	}).Info("Finished initialization.")
}

func handler(ctx context.Context, event events.APIGatewayCustomAuthorizerRequest) (
	events.APIGatewayCustomAuthorizerResponse, error) {
	log.WithField("method_arn", event.MethodArn).Info("Authorizer lambda invoked.")

	policy := &Policy{
		Context: sharedContext,
	}

	responseBuilder := &cognitoAuthorizer.ResponseBuilder{
		Context:        sharedContext,
		PolicyBuilder:  policy,
		ContextBuilder: policy,
	}

	return responseBuilder.BuildResponse(event.AuthorizationToken)
}

func main() {
	lambda.Start(handler)
}

Authors

  • Grzegorz Bednarski, Nordcloud 🇵🇱
  • Kamil Piotrowski, Nordcloud 🇵🇱
  • Artur Kowalski, Nordcloud 🇵🇱

About

Build your AWS API Gateway custom authorizer lambda without the need to handle tokens by yourself. Just implement the logic...

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •