AWS for Games Blog

Using Amazon Cognito to Authenticate Players for a Game Backend Service

Authored by Chanh Tran, Pawan Matta, and Zack Anderson

Player authentication is an integral part of online multiplayer games. This is especially true when key concepts of the player experience, such as player progression, digital entitlements, and even monetary value, are inextricably tied to a player’s account.

Amazon Cognito is a simple user sign-up, sign-in, and access control service. In this guide, we will demonstrate how to create a simple player authentication service using Amazon Cognito as the core service as well as AWS Lambda and Amazon API Gateway as ancillary services. This example player authentication implementation is primarily intended to be built upon as a part of a game backend service solution.

Before diving into the implementation, there are a few considerations to keep in mind:

  • This solution uses proxy integration with REST APIs and therefore is game engine agnostic. You will have to make REST API calls from your game client to use this implementation.
  • This solution uses Amazon Cognito user pool as the user directory and will not federate users from other identity providers.
  • Watch this YouTube tutorial series for a full implementation of this solution with Amazon GameLift and Unreal Engine 4, or skip to episode 6 of the series for a video guide of this player authentication implementation.

Architecture

Cognito architecture

A: Game client make REST API call to unauthenticated endpoint to invoke Login Lambda function with username and password in JSON body.
B: Login Lambda function uses username and password to authenticate with Amazon Cognito user pool and obtains IdToken.
C: Login Lambda function sends IdToken back to game client through the API Gateway.
D: Game client makes a REST API call to Amazon API Gateway which will validate the IdToken with the Cognito authorizer. API Gateway will then invoke the backend service Lambda function.

Amazon Cognito

The two main components of Amazon Cognito are user pools and identity pools. User pools are user directories that provide sign-up and sign-in options for your app users. Identity pools enable you to grant your users access to other AWS services. For this guide, we will use user pools as the identity provider.

  1. Create a user pool
    1. Name it ExampleUserpool
    2. Use default settings, click Review defaults
    3. Click Create pool
  2. Create App Client
    1. Name it ExampleGameClient
    2. Uncheck Generate client secret
    3. Uncheck Enable lambda trigger based custom authentication (ALLOW_CUSTOM_AUTH)
    4. Uncheck Enable SRP (secure remote password) protocol based authentication (ALLOW_USER_SRP_AUTH)
    5. Check Enable username password based authentication (ALLOW_USER_PASSWORD_AUTH)
    6. Click Create App ClientApp client name
  3. Create Hosted UI
    1. Click on App client settings
    2. Enabled Identity Providers, check Cognito User Pool
    3. Input “https://thinkwithwp.com/” or your own website for the Callback URL and Sign out URL.
      1. The Callback URL is the address where Cognito will redirect after a successful login
      2. The Sign out URL is the address where Cognito will redirect after a logout
    4. Allowed OAuth Flow, check Implicit grant
    5. Allowed OAuth Scopes, check email and openid
    6. Save changes
  4. Create a Cognito domain name
    1. Click on Domain name
    2. Input unique subdomain name and Save changes
    3. Go back to App client setting and click Launch Hosted UI
  5. Create a new test user in the Hosted UI
    1. Click Sign up
    2. Input a username, email, and password for your test user
    3. Verify your email to confirm your test user account

AWS Lambda

AWS Lambda is an event-driven, serverless compute service that allows you to run code for a backend service without provisioning and managing servers. We will create Lambda functions for user login then integrate it with Amazon API Gateway to serve as our game backend service.

Login Lambda function

  • Create new Lambda function
    1. Go to the Lambda console
    2. Select Author from scratch
    3. Function name: CognitoLogin
    4. Runtime: Python 3.8
    5. Click Create function
    6. Copy and paste the following code into your Lambda function
    7. Create an environment variable named USER_POOL_APP_CLIENT_ID
    8. Paste in App client id from Cognito user pool into the USER_POOL_APP_CLIENT_ID environment variable
    9. Click Deploy
  • Login Lambda function code, this sample code could also be found in this GitHub

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0

import boto3
import os
import sys

# TODO Set to your created app client id
USER_POOL_APP_CLIENT_ID = os.environ['USER_POOL_APP_CLIENT_ID']

client = boto3.client('cognito-idp')

def lambda_handler(event, context):
    if 'username' not in event or 'password' not in event:
        return {
            'status': 'fail',
            'msg': 'Username and password are required'
        }
    resp, msg = initiate_auth(event['username'], event['password'])
    if msg != None:
        return {
            'status': 'fail',
            'msg': msg
        }
    return {
        'status': 'success',
        'tokens': resp['AuthenticationResult']
    }

def initiate_auth(username, password):
    try:
        resp = client.initiate_auth(
            ClientId=USER_POOL_APP_CLIENT_ID,
            AuthFlow='USER_PASSWORD_AUTH',
            AuthParameters={
                'USERNAME': username,
                'PASSWORD': password
            })
    except client.exceptions.InvalidParameterException as e:
        return None, "Username and password must not be empty"
    except (client.exceptions.NotAuthorizedException, client.exceptions.UserNotFoundException) as e:
        return None, "Username or password is incorrect"
    except Exception as e:
        print("Uncaught exception:", e, file=sys.stderr)
        return None, "Unknown error"
    return resp, None

  • Change Lambda function permissions
    1. Click on Configuration tab, click on Permissions to open IAM console
    2. Add Inline policy, click on JSON editor
    3. Copy and paste in the IAM policy from below
    4. Click Review policy and name the policy
    5. Click Create

Login Lambda function IAM policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "cognito-idp:InitiateAuth",
            "Resource": "*"
        }
    ]
}

  • Test the Lambda function
    1. In the Lambda console, click Test
    2. Create new test event with the hello-world event template
    3. Name the event login-success
    4. Change the JSON payload to send the username and password of the Cognito test user. An example of the test event can be seen below.
    5. Click Create
    6. Create another test event but name it login-fail and change the password an incorrect password
    7. Test both events

Test event

{
    “username”: “testuser”,
    “password”: “password”
}

ExampleBackend Lambda function

For this guide, this Lambda function will only return a simple hello message. To add other features to your game backend service, such as writing to a database, you will have to create a different Lambda function for each feature.

  1. Create new Lambda function
    1. Go to the Lambda console
    2. Select Author from scratch
    3. Function name: ExampleBackend
    4. Runtime: Python 3.8
    5. Click Create function
  2. Change the return message to “Hello from ExampleBackend Lambda”
  3. Click Deploy

ExampleBackend Lambda function code

import json

def lambda_handler(event, context):
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from ExampleBackend Lambda!')
    }

Amazon API Gateway

Amazon API Gateway is service for creating, publishing, maintaining, monitoring, and securing REST, HTTP, and WebSocket APIs. For this guide, we will use API Gateway to create REST API endpoints that applications can call to invoke Lambda functions.

  1. Create the REST API
    1. Go to the API Gateway console
    2. Click Build under REST API (public) option
    3. Select New API
    4. Name the API
    5. Click Create
  2. Create the Resource and Method
    1. With the root of the API selected, click Actions, select Create Resource
    2. Name it login
    3. Click Create Resource
    4. With the login Resource selected, click Actions, select Create Method
    5. Select POST and click the green check mark
    6. Integration type: Lambda Function
    7. Lambda Function: CognitoLogin
    8. Click Save
  3. Create another Resource and Method that requires authentication
    1. With the root of the API selected, click Actions, select Create Resource
    2. Name it example
    3. Click Create Resource
    4. With the login Resource selected, click Actions, select Create Method
    5. Select POST and click the green check mark
    6. Integration type: Lambda Function
    7. Lambda Function: ExampleBackend
    8. Click Save
  4. Create Cognito Authorizer
    1. On the left navigation pane, click Authorizers
    2. Name the Authorizer, “cognito-authorizer”
    3. Type: Select Cognito
    4. Select the Cognito User Pool you just created
    5. For token Source, type in “Authorization”
    6. Click Create
  5. Add Cognito authorizer to Method
    1. On the left navigation pane, click Resources
    2. Select the Method that requires authentication
    3. Click Method Request
    4. Under Setting, edit Authorization
    5. Select the Cognito Autorizer you just created and click the update check mark
  6. Deploy the API
    1. Click Actions and select Deploy API
    2. Deployment stage: [New Stage]
    3. Stage name: test
    4. Click Deploy
    5. Take note of the Invoke URL

API Testing

Since the API Gateway endpoints are public, you can make those API request using curl through a regular Windows command prompt, Mac terminal, or any shell with curl.

  • Use curl command to test /login API
      1. Open CloudShell, Command Prompt, Mac terminal, or any shell with curl.
      2. Make sure you type in the correct password and paste in the invoke URL from API Gateway into your REST API call.

curl command for /login API call

curl -X POST --data '{"username": "testuser", "password": "<correctPassword>"}' https://<invoke-url>/login

  • Use curl command to test /example API
    1. Copy the IdToken from the Login function’s response and paste it into the /example REST API call.

curl command for /example API call

curl -X GET -H "Authorization: Bearer <IdTokenhere>" https://<invoke-url/example

Technical Considerations

    • This solution does not use refresh tokens. It is possible to implement the use of refresh tokens for the ALLOW_REFRESH_TOKEN_AUTH auth flow should you wish to allow players to re-login without providing their username and password again. For more information, see Using the refresh token. If you plan to use refresh tokens, you would also need to implement token revocation. For more information, see Revoking tokens.
    • This solution does not use access tokens. Access tokens in conjunction with Cognito Identity Pools are used to grant clients access to AWS services. However, we will not grant clients direct access to AWS and any API calls to AWS services should be handled through a backend service.
    • Players will have to use the Cognito Hosted UI to sign up, it is possible to customize this Hosted UI. For more information, see Customizing the built-in webpages.

Conclusion
Thank you for following this guide on how to use Amazon Cognito to authenticate players for a game backend service. To stay up to date on the latest tutorials on how to use AWS for GameTech, follow the AWS GameTech channel on YouTube!