Skip to content

Commit

Permalink
Enable Google Cloud Identity Platform for staging and production (#698)
Browse files Browse the repository at this point in the history
* Enable Google Cloud Identity Platform for staging and production

This change configures Google Cloud Identity Platform (GCIP) for staging and production environments, enabling developers to deploy full-stack applications with GCIP to GCP.

Key changes include:

- Setting up a new GCIP tenant with a GitHub Provider for authentication.
- Modifying the frontend infrastructure module to utilize the new tenant and enable GitHub login.
- Updating frontend code to handle optional tenant IDs, allowing seamless transition between local and cloud environments.

This addresses the previous limitation of having GCIP configured only locally with the emulator.

* use console.cloud.google.com for links
  • Loading branch information
jcscottiii authored Sep 26, 2024
1 parent df1924c commit 56221cc
Show file tree
Hide file tree
Showing 19 changed files with 220 additions and 3 deletions.
13 changes: 12 additions & 1 deletion DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,15 @@ instructions assume you have access to the following projects:
- webstatus-dev-internal-staging
- webstatus-dev-public-staging
- production
- web-compass-staging
- web-compass-prod
- webstatus-dev-internal-prod
- webstatus-dev-public-prod

Google Cloud Identity Platform:

- [Enable](https://console.cloud.google.com/marketplace/details/google-cloud-platform/customer-identity) Cloud Identity Platform for the internal project.
- [Enable](https://cloud.google.com/identity-platform/docs/multi-tenancy-quickstart) multi-tenancy in the Google Cloud Console.

## Deploying your own copy

```sh
Expand Down Expand Up @@ -83,6 +88,10 @@ Or you could populate with fake data by running.
go run ./util/cmd/load_fake_data/main.go -spanner_project=${SPANNER_PROJECT_ID} -spanner_instance=${SPANNER_INSTANCE_ID} -spanner_database=${SPANNER_DATABASE_ID} -datastore_project=${DATASTORE_PROJECT_ID} -datastore_database=${DATASTORE_DATABASE}
```

Setup auth:

Add your domain to the allow-list of domains in the [console](https://console.cloud.google.com/customer-identity/settings?project=webstatus-dev-internal-staging).

When you are done with your own copy

```sh
Expand All @@ -96,6 +105,8 @@ terraform workspace select default
terraform workspace delete $ENV_ID
```
Also, remove your domain from the allow-list of domains in the [console](https://console.cloud.google.com/customer-identity/settings?project=webstatus-dev-internal-staging).
## Deploy Staging
```sh
Expand Down
1 change: 1 addition & 0 deletions frontend/src/common/app-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface FirebaseAppSettings {

interface FirebaseAuthSettings {
emulatorURL: string;
tenantID: string;
}

interface FirebaseSettings {
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@
"authDomain": "$FIREBASE_APP_AUTH_DOMAIN"
},
"auth": {
"emulatorURL": "$FIREBASE_AUTH_EMULATOR_URL"
"emulatorURL": "$FIREBASE_AUTH_EMULATOR_URL",
"tenantID": "$FIREBASE_AUTH_TENANT_ID"
}
}
}'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ describe('webstatus-app', () => {
},
auth: {
emulatorURL: 'http://localhost:9099',
tenantID: 'tenantID',
},
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ describe('WebstatusServiceContainer', () => {
},
auth: {
emulatorURL: 'http://localhost:9099',
tenantID: 'tenantID',
},
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ describe('webstatus-app-settings-service', () => {
},
auth: {
emulatorURL: 'http://localhost:9099',
tenantID: 'tenantID',
},
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class FakeParentElement extends LitElement {
describe('webstatus-firebase-auth-service', () => {
const settings = {
emulatorURL: '',
tenantID: 'tenantID',
};
it('can be added to the page with the settings', async () => {
const component = await fixture<WebstatusFirebaseAuthService>(
Expand Down Expand Up @@ -144,6 +145,11 @@ describe('webstatus-firebase-auth-service', () => {
'github',
'icon should be github'
);
assert.equal(
component.firebaseAuthConfig?.auth.tenantId,
'tenantID',
'unexpected tenantID'
);
// Ensure it gets it via context.
assert.equal(
component.firebaseAuthConfig,
Expand Down Expand Up @@ -193,6 +199,7 @@ describe('webstatus-firebase-auth-service', () => {
const testSettings = {
// Set emulator URL
emulatorURL: 'http://localhost:9099',
tenantID: '',
};
const root = document.createElement('div');
document.body.appendChild(root);
Expand Down Expand Up @@ -228,6 +235,10 @@ describe('webstatus-firebase-auth-service', () => {
await component.updateComplete;

assert.isTrue(emulatorConnectorStub.calledOnce);
assert.notExists(
component.firebaseAuthConfig?.auth.tenantId,
'unexpected tenantID'
);
expect(emulatorConnectorStub).to.have.been.calledWith(
authStub,
'http://localhost:9099'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {ServiceElement} from './service-element.js';

interface FirebaseAuthSettings {
emulatorURL: string;
tenantID: string;
}

@customElement('webstatus-firebase-auth-service')
Expand All @@ -62,6 +63,10 @@ export class WebstatusFirebaseAuthService extends ServiceElement {
initFirebaseAuth() {
if (this.firebaseApp) {
const auth = this.authInitializer(this.firebaseApp);
// Local environment will not have a tenantID.
if (this.settings.tenantID !== '') {
auth.tenantId = this.settings.tenantID;
}
const provider = new GithubAuthProvider();
this.firebaseAuthConfig = {
auth: auth,
Expand Down
7 changes: 7 additions & 0 deletions infra/.envs/prod.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,10 @@ wpt_region_schedules = {
"us-central1" = "0 21 * * *" # Daily at 9:00 PM
"europe-west1" = "0 9 * * *" # Daily at 9:00 AM
}

firebase_api_key_location = "prod-firebase-app-api-key"

auth_github_config_locations = {
client_id = "prod-github-client-id"
client_secret = "prod-github-client-secret"
}
7 changes: 7 additions & 0 deletions infra/.envs/staging.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,10 @@ wpt_region_schedules = {
"us-central1" = "0 21 * * *" # Daily at 9:00 PM
"europe-west1" = "0 9 * * *" # Daily at 9:00 AM
}

firebase_api_key_location = "staging-firebase-app-api-key"

auth_github_config_locations = {
client_id = "staging-github-client-id"
client_secret = "staging-github-client-secret"
}
44 changes: 44 additions & 0 deletions infra/auth/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

locals {
gh_client_id = sensitive(data.google_secret_manager_secret_version_access.gh_client_id.secret_data)
gh_client_secret = sensitive(data.google_secret_manager_secret_version_access.gh_client_secret.secret_data)
}

data "google_secret_manager_secret_version_access" "gh_client_id" {
provider = google.internal_project
secret = var.github_config_locations.client_id
}

data "google_secret_manager_secret_version_access" "gh_client_secret" {
provider = google.internal_project
secret = var.github_config_locations.client_secret
}

resource "google_identity_platform_tenant" "tenant" {
provider = google.internal_project
display_name = var.env_id
allow_password_signup = false
enable_email_link_signin = false
}

resource "google_identity_platform_tenant_default_supported_idp_config" "github_idp_config" {
provider = google.internal_project
enabled = true
tenant = google_identity_platform_tenant.tenant.name
idp_id = "github.com"
client_id = local.gh_client_id
client_secret = local.gh_client_secret
}
18 changes: 18 additions & 0 deletions infra/auth/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


output "tenant_id" {
value = google_identity_platform_tenant.tenant.name
}
24 changes: 24 additions & 0 deletions infra/auth/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

terraform {
required_providers {
google = {
source = "hashicorp/google"
configuration_aliases = [
google.internal_project,
]
}
}
}
25 changes: 25 additions & 0 deletions infra/auth/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

variable "env_id" {
type = string
}

variable "github_config_locations" {
description = "Location of the github configuration in secret manager"
type = object({
client_id = string
client_secret = string
})
}
12 changes: 12 additions & 0 deletions infra/frontend/service.tf
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,18 @@ resource "google_cloud_run_v2_service" "service" {
name = "PROJECT_ID"
value = data.google_project.datastore_project.number
}
env {
name = "FIREBASE_APP_AUTH_DOMAIN"
value = var.firebase_settings.auth_domain
}
env {
name = "FIREBASE_APP_API_KEY"
value = var.firebase_settings.api_key
}
env {
name = "FIREBASE_AUTH_TENANT_ID"
value = var.firebase_settings.tenant_id
}
}
vpc_access {
network_interfaces {
Expand Down
8 changes: 8 additions & 0 deletions infra/frontend/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,11 @@ variable "projects" {
variable "deletion_protection" {
type = bool
}

variable "firebase_settings" {
type = object({
auth_domain = string
api_key = string
tenant_id = string
})
}
23 changes: 23 additions & 0 deletions infra/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.

locals {
firebase_api_key = sensitive(data.google_secret_manager_secret_version_access.firebase_api_key.secret_data)
}

data "google_secret_manager_secret_version_access" "firebase_api_key" {
provider = google.internal_project
secret = var.firebase_api_key_location
}

module "auth" {
source = "./auth"
providers = {
google.internal_project = google.internal_project
}
env_id = var.env_id
github_config_locations = var.auth_github_config_locations
}

module "services" {
source = "./services"
providers = {
Expand Down Expand Up @@ -118,4 +136,9 @@ module "frontend" {
ssl_certificates = var.ssl_certificates
domains_for_gcp_managed_certificates = var.frontend_domains_for_gcp_managed_certificates
projects = var.projects
firebase_settings = {
api_key = local.firebase_api_key
auth_domain = "${var.projects.internal}.firebaseapp.com"
tenant_id = module.auth.tenant_id
}
}
5 changes: 4 additions & 1 deletion infra/providers.tf
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ provider "google" {
provider "google" {
alias = "internal_project"
project = var.projects.internal
# Need user_project_override=true for identity platform
# https://stackoverflow.com/a/78203631
user_project_override = true
}

provider "google" {
Expand All @@ -46,4 +49,4 @@ provider "docker" {
address = module.storage.docker_repository_details.hostname
config_file = pathexpand("~/.docker/config.json")
}
}
}
14 changes: 14 additions & 0 deletions infra/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,17 @@ variable "chromium_region_schedules" {
variable "web_features_region_schedules" {
type = map(string)
}


variable "firebase_api_key_location" {
description = "Location of the firebase api key in secret manager"
type = string
}

variable "auth_github_config_locations" {
description = "Location of the github configuration in secret manager"
type = object({
client_id = string
client_secret = string
})
}

0 comments on commit 56221cc

Please sign in to comment.