Skip to content

Commit

Permalink
Move OAuth Setup to a manual process (#98)
Browse files Browse the repository at this point in the history
* Move OAuth Setup to a manual process

Moving the manual setup to a manual process, and setup initial callback
URL using sslip.io.

In the process also included:

* Improved error logging for failure to login.
* Updated README.md instructions.
* Set fixed size on launcher window, also center on screen
  (mainly because I use a tiling window manager)
* Use a static IP for the frontend service so we can template out
  config variables.

Work on #86

* Fix single quoted syntax error.

---------

Co-authored-by: Ben Huston <[email protected]>
  • Loading branch information
markmandel and bbhuston authored Mar 5, 2023
1 parent d208a89 commit d5ceaa8
Show file tree
Hide file tree
Showing 14 changed files with 130 additions and 67 deletions.
69 changes: 59 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,28 @@
This multiplayer demo is a cloud first implementation of a global scale, realtime multiplayer game utilising
dedicated game servers, utlising both Google Cloud's products and open source gaming solutions.

## OAuth Authentication

We need to manually set up the OAuth authentication, as unfortunately this cannot be automated.

The details, such as name and email address of both of these steps don't matter, so feel free to use something
arbitrary for any part not specified.

Open the [Google OAuth consent screen](https://console.cloud.google.com/apis/credentials/consent) for your project,
and create an "External" App, and allowlist any users you wish to be able to login to your deployment of this game.

Open the [Google Credentials](https://console.cloud.google.com/apis/credentials) screen for your project, and click
"+ CREATE CREDENTIALS", and create an "OAuth Client ID" of type "Web Application".

Leave this page open, as we'll need the Client ID and Client secret of the ID you just created shortly.

## Infrastructure and Services

### Prerequisites

To run the Game Demo install, you will need the following applications installed on your workstation:

* A Google Cloud Project
* [Terraform](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli)
* [Google Cloud CLI](https://cloud.google.com/sdk/docs/install)

Expand Down Expand Up @@ -57,11 +73,18 @@ NOTE: The GCS bucket does not have to exist in the same Google project as the Gl
cd $GAME_DEMO_HOME/infrastructure
terraform init
cp terraform.tfvars.sample terraform.tfvars

# Edit terraform.tfvars as needed, especially <PROJECT_ID>.
# Setting `apply_org_policies = true` will also apply any neccessary GCP Org Policies as part of the provioning process.
```

You will need to now edit `terraform.tfvars`

* Update <PROJECT_ID> with the ID of your Google Cloud Project,
* Updated <CLIENT_ID> and <CLIENT_SECRET> with the Client ID and Client secret created in the above step.
* Setting `apply_org_policies = true` will also apply any necessary GCP Org Policies as part of the provisioning
process.

You can edit other variables in this file, but we recommend leaving the default values for your first run before
experimenting.

### Provision the infrastructure.

```shell
Expand All @@ -70,19 +93,26 @@ terraform apply

### OAuth Authentication

Terraform is only able to make an [Internal Oauth consent screen](https://support.google.com/cloud/answer/10311615),
which means that only users from your Google organisation will be able to authenticate against the project when
using logging in via the Game Launcher.
We now need to update our OAuth authentication configuration with the address of our authenticating frontend API.

Let's grab the IP for that API, by running:

```shell
gcloud compute addresses list --filter=name=frontend-service --format="value(address)"
```

This should give you back an IP, such as `35.202.107.204`.

1. Click "+ ADD URI" under "Authorised JavaScript origins" and add "http://[IP_ADDRESS].sslip.io".
2. Click "+ ADD URI" under "Authorised redirect URIs" and add "http://[IP_ADDRESS].sslip.io/callback"
3. Click "Save".

You can manually move the consent screen to External (Testing), such that you can allow list accounts outside your
organisation to be able to authenticate against the project, but that has to be a manual step through the
[OAuth Consent screen](https://console.cloud.google.com/apis/credentials/consent).
Since OAuth needs a domain to authenticate against, we'll use [sslip.io](https://sslip.io) for development purposes.

### Deploy Agones To Agones GKE Clusters

The Agones deployment is in two steps: The Initial Install and the Allocation Endpoint Patch.


### Initial Install
Replace the` _RELEASE_NAME` substitution with a unique build name. Cloudbuild will deploy Agones using Cloud Deploy.

Expand Down Expand Up @@ -137,6 +167,25 @@ Open the [`game`](./game) folder in Unreal Engine. Once finished opening, you ca
the editor (Hit the ▶️ button), or we can package the project via: Platforms > {your host platform} > Package Project,
and execute the resultant package.

## Run the Game Launcher

{TODO: still requires more details}

```shell
cd $GAME_DEMO_HOME/game/GameLauncher
# Grab the IP Address again of our frontend service, so we can use it
gcloud compute addresses list --filter=name=frontend-service --format="value(address)"
```

Edit the app.ini, and replace the `frontend_api` value with http://[IP_ADDRESS].sslip.io

The run:

```shell
go run main.go
```

### Troubleshooting

##### This project was made with a different version of the Unreal Engine.
Expand Down
20 changes: 15 additions & 5 deletions game/GameLauncher/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
## Generic game launcher with "Sign-in with google" authentication.

For prerequisites check here:
https://developer.fyne.io/started/
To run the launcher locally:

For packaging check here:
https://developer.fyne.io/started/packaging
```shell
go run main.go
```

To build locally after installing necessary dependencies just run:

`go build .`
```shell
go build .
```

Move the built binary together with `app.ini` and `assets/` to the game client folder. Launch there.

`app.ini` contains the configuration endpoint for the Frontend API as well as executable names for the game client.

If you want to fully package the launcher:

For prerequisites check here:
https://developer.fyne.io/started/

For packaging check here:
https://developer.fyne.io/started/packaging
2 changes: 1 addition & 1 deletion game/GameLauncher/app.ini
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ callback_listen_port = 8082
binary = DroidShooterClient.exe

[linux]
binary = DroidShooterClient
binary = DroidShooterClient.sh

[darwin]
binary = DroidShooterClient
17 changes: 8 additions & 9 deletions game/GameLauncher/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"io"
"log"
"net/http"
"os"
Expand Down Expand Up @@ -63,7 +63,9 @@ func main() {
// UI
myApp = app.New()
myWindow = myApp.NewWindow("Google for Games Launcher")
myWindow.SetFixedSize(true)
myWindow.Resize(fyne.NewSize(320, 260))
myWindow.CenterOnScreen()

image := canvas.NewImageFromFile("assets/header.png")
image.FillMode = canvas.ImageFillContain
Expand Down Expand Up @@ -180,19 +182,16 @@ func getPlayerName() string {
if err != nil {
log.Fatal(err)
}

if response.StatusCode != 200 {
log.Fatal("Unable to fetch user information. Expired token?")
}

defer response.Body.Close()
// Use response.Body to get user information.

data, err := ioutil.ReadAll(response.Body)
data, err := io.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}

if response.StatusCode != 200 {
log.Fatalf("Unable to fetch user information. Expired token?: %s", data)
}

var result map[string]interface{}
if err := json.Unmarshal(data, &result); err != nil {
log.Fatalf("Unable to decode json: %s", err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,21 @@ data:
CLIENT_ID: ${client_id}
CLIENT_SECRET: ${client_secret}
LISTEN_PORT: "8080"
CALLBACK_HOSTNAME: http://${service_address}.sslip.io/callback
CLIENT_LAUNCHER_PORT: "8082"
PROFILE_SERVICE: http://profile
PING_SERVICE: http://ping-discovery
JWT_KEY: ${jwt_key}
---
apiVersion: v1
kind: Service
metadata:
name: frontend
spec:
type: LoadBalancer
selector:
app: frontend
ports:
- port: 80
targetPort: 8080
loadBalancerIP: ${service_address}
16 changes: 8 additions & 8 deletions infrastructure/org-policies.tf
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Optionally apply these Org Policies, as specified in terraform.tfvars file

module "gcp_org_policy_v2_requireShieldedVm" {
source = "terraform-google-modules/org-policy/google//modules/org_policy_v2"
version = "~> 5.2.0"
source = "terraform-google-modules/org-policy/google//modules/org_policy_v2"
version = "~> 5.2.0"

count = var.apply_org_policies == true ? 1 : 0
policy_root = "project"
Expand All @@ -18,8 +18,8 @@ module "gcp_org_policy_v2_requireShieldedVm" {
}

module "gcp_org_policy_v2_disableServiceAccountKeyCreation" {
source = "terraform-google-modules/org-policy/google//modules/org_policy_v2"
version = "~> 5.2.0"
source = "terraform-google-modules/org-policy/google//modules/org_policy_v2"
version = "~> 5.2.0"

count = var.apply_org_policies == true ? 1 : 0
policy_root = "project"
Expand All @@ -35,8 +35,8 @@ module "gcp_org_policy_v2_disableServiceAccountKeyCreation" {
}

module "gcp_org_policy_v2_vmCanIpForward" {
source = "terraform-google-modules/org-policy/google//modules/org_policy_v2"
version = "~> 5.2.0"
source = "terraform-google-modules/org-policy/google//modules/org_policy_v2"
version = "~> 5.2.0"

count = var.apply_org_policies == true ? 1 : 0
policy_root = "project"
Expand All @@ -52,8 +52,8 @@ module "gcp_org_policy_v2_vmCanIpForward" {
}

module "gcp_org_policy_v2_vmExternalIpAccess" {
source = "terraform-google-modules/org-policy/google//modules/org_policy_v2"
version = "~> 5.2.0"
source = "terraform-google-modules/org-policy/google//modules/org_policy_v2"
version = "~> 5.2.0"

count = var.apply_org_policies == true ? 1 : 0
policy_root = "project"
Expand Down
2 changes: 1 addition & 1 deletion infrastructure/providers.tf
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.

provider "google" {
project = var.project
project = var.project
user_project_override = true
}

Expand Down
31 changes: 15 additions & 16 deletions infrastructure/services-gke.tf
Original file line number Diff line number Diff line change
Expand Up @@ -118,29 +118,28 @@ resource "local_file" "services-ping-service-account" {
}

#
# OAuth Credentials for the Frontend Service
# Frontend Service
#

resource "google_iap_brand" "project_brand" {
support_email = "[email protected]"
application_title = "Global Game Demo"
project = var.project
resource "google_compute_address" "frontend-service" {
project = var.project
provider = google-beta # so we can do labels

depends_on = [google_project_service.project]
}
region = var.services_gke_config.location
name = "frontend-service"

resource "google_iap_client" "project_client" {
display_name = "Global Game Client"
brand = google_iap_brand.project_brand.name
labels = {
"environment" = var.resource_env_label
}
}

# Make the environment configmap for the front service
resource "local_file" "services-frontend-config-map" {
content = templatefile(
"${path.module}/files/services/frontend-configmap.yaml.tpl", {
client_id = google_iap_client.project_client.client_id
client_secret = google_iap_client.project_client.secret
jwt_key = var.frontend-service.jwt_key
"${path.module}/files/services/frontend-config.yaml.tpl", {
service_address = google_compute_address.frontend-service.address
client_id = var.frontend-service.client_id
client_secret = var.frontend-service.client_secret
jwt_key = var.frontend-service.jwt_key
})
filename = "${path.module}/${var.services_directory}/frontend/configmap.yaml"
filename = "${path.module}/${var.services_directory}/frontend/config.yaml"
}
4 changes: 3 additions & 1 deletion infrastructure/terraform.tfvars.sample
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ services_gke_config = {

# Frontend Service Config Values
frontend-service = {
jwt_key = "r@nd0m$"
client_id = "CLIENT_ID"
client_secret = "CLIENT_SECRET"
jwt_key = "r@nd0m$"
}

# Artifact Registry variables
Expand Down
4 changes: 3 additions & 1 deletion infrastructure/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ variable "k8s_service_account_id" {

variable "frontend-service" {
type = object({
jwt_key = string
client_id = string
client_secret = string
jwt_key = string
})
description = "Configuration for the frontend service that provides oAuth authentications"
}
Expand Down
2 changes: 1 addition & 1 deletion services/frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.

configmap.yaml
config.yaml
12 changes: 0 additions & 12 deletions services/frontend/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,3 @@ spec:
envFrom:
- configMapRef:
name: frontend-service
---
apiVersion: v1
kind: Service
metadata:
name: frontend
spec:
type: LoadBalancer
selector:
app: frontend
ports:
- port: 80
targetPort: 8080
2 changes: 1 addition & 1 deletion services/profile/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ kind: Service
metadata:
name: profile
spec:
type: LoadBalancer
type: ClusterIP
selector:
app: profile
ports:
Expand Down
2 changes: 1 addition & 1 deletion services/skaffold.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ deploy:
- ping-discovery/deployment.yaml
- profile/spanner_config.yaml
- profile/deployment.yaml
- frontend/config.yaml
- frontend/deployment.yaml
- frontend/configmap.yaml

0 comments on commit d5ceaa8

Please sign in to comment.