Common Hosted Single Sign-On (CSS) is an self-serve application developed by Pathfinder SSO Team to enable digital product development teams to request integrations with BC Government approved login options such as IDIR, BCeID and more.
The general workflow for new client creation is:
lambda/app
) evaluates the data and after successful validation, the integration is added to a queue table in AWS RDS. If this process fails then the integration status is updated to planFailed
applied
. If the client creation fails at keycloak, then the integration remains in queue table and its status is updated to applyFailed
.lambda/app
. The successful integrations are removed from queue and updated with applied
status.data-integrity
lambda function runs daily in the morning. It scans the CSS database for active integrations and lists out any missing clients from Keycloak and notifies the Pathfinder SSO team via Rocket.Chat
../terraform
holds the terraform code to deploy the CSS backend and the grafana instances in AWS environment.The front end application is built with nextjs (a react framework). It is a static application hosted on github pages. A static single-page-application was chosen to deliver a fast user experience, take advantage of pre-built bcgov components, and to interact as an independant layer from the backend.
The backend uses AWS serverless technologies, which was chosen to reduce cost (with serverless, you only pay for what you use) since this application is expected to have low traffic. It uses:
Only users with IDIR with or without MFA are allowed to login and request for an integration.
admin
or member
roleFor getting details on IDIR users (e.g valid user lookups, importing into keycloak) we are using Microsoft Graph. For details on implementation see here.
To setup a local development environment, a detailed guide may be found here.
This repository requires the following secrets:
API_AUTH_SECRET
: The basic secret for endpoint to remove inactive IDIR users from CSS
AWS_ECR_URI
: AWS container registry URI
CHES_USERNAME
: Username of CHES account
CHES_PASSWORD
: Password of CHES account
KEYCLOAK_V2_DEV_URL
: The URL of the keycloak dev environment.
KEYCLOAK_V2_TEST_URL
: The URL of the keycloak test environment.
KEYCLOAK_V2_PROD_URL
: The URL of the keycloak prod environment.
KEYCLOAK_V2_DEV_USERNAME
: The client id for the keycloak dev environment.
KEYCLOAK_V2_TEST_USERNAME
: The client id for the keycloak test environment.
KEYCLOAK_V2_PROD_USERNAME
: The client id for the keycloak prod environment.
KEYCLOAK_V2_DEV_PASSWORD
: The client secret for the keycloaks dev environment.
KEYCLOAK_V2_TEST_PASSWORD
: The client secret for the keycloaks test environment.
KEYCLOAK_V2_PROD_PASSWORD
: The client secret for the keycloaks prod environment.
GH_ACCESS_TOKEN
: Access token for the github service account (used to trigger workflows in sso-terraform
).
GH_SECRET
: The secret used to authorize requests between github actions (from sso-terraform
) to the API.
TFC_TEAM_TOKEN
: The token used to run terraform cloud.
GRAFANA_API_TOKEN
: The API token to authenticate with Grafana and pull metrics and logs data.
TERRAFORM_DEPLOY_ROLE_ARN
: The AWS role arn required by AWS SDK in terraform github workflow to authenticate with AWS and update resources.
In Addition, the following secrets are required for sso-terraform
and sso-terraform-dev
to work with this app:
GH_ACCESS_TOKEN
: Access token for the github service account (used to run github API calls).
GH_SECRET
: The secret used to authorize requests between github actions (from sso-terraform
) to the API.
REALM_REGISTRY_API
: The API URL of Realm Registry
VERIFY_USER_SECRET
: A secret or a private key used for signing team invitation tokens
To run this app, you will need to setup your terraform infrastructure. The code in the terraform directory
assumes one available VPC exists with available subnets in two zones. To set the names for your subnets the terraform variables
subnet_a
and subnet_b
should be set.
The lambda functions are written in typescript, and to compile and bundle them you can run make lambda-all
from the lambda directory.
Once they are compiled, from the terraform directory you can run terraform apply
to build the backend.
This repository has frontend tests using jest and react testing library, and backend tests run with jest.
Tests are run in CI in the test.yml
file.
To run the frontend tests, from the app
directory run yarn test
. If adding a snapshot test,
running yarn test
will add the new snapshot file for you. If you want to update a snapshot test,
run yarn test -u
To run the backend unit tests,
you will need to have a local postgres database running.
Ensure you have started the server with pg_ctl start
For the first time running the tests, the database will need to be created.
Run make local_db
from the root directory
Note: you may need to run chmod +x ./.bin/db-setup.sh
to give necessary permissions.
Run make server_test
to run all the backend unit tests
Navigate to ./lambda/jest_stare
directory and open the index.html
to view the report that shows the code coverage
Error codes we use for the application:
E01
: There is an application in the sso-terraform
repository that cannot be applied, blocking new requestsE02
: The user has a token in their session storage that is invalidE03
: Missing of invalid email address associated with your IDIR accountE04
: Request timeoutE05
: 503 Service Temporarily UnavailableTo use, you will need access to the AWS platform as well as the database credentials.
RDS
dashboard and select DB Clusters from the resources panel.aurora-db-postgres
. From the top right actions
dropdown select query.connect
(it may take some time to connect)There are two instances of Grafana, one for sandbox and the other for production environments. Both of them are deployed in AWS ECS containers with persistent storage through AWS EBS under environment specific VPCs. Grafana has read only access to the database.
Some queries are id specific, update the where clause to change the request number_
-- Count of clients in draft
select count(*) from requests where status='draft';
-- Count of clients awaiting approval (note: currently auto-approve but will apply when bceid is added)
select count(*) from requests where status='submitted';
-- Count of clients completed
select count(*) from requests where status='applied';
-- (initial )time request was fulfilled (dev, test, and prod)
select events.created_at from events join requests on requests.id = events.request_id where requests.id=1 and events.event_code = 'request-apply-success';
-- get all clients. note the null are those that have not submittted request (zs)
select client_name, preferred_email from requests where archived = false;
Queries to fetch user emails
-- Integration users with a team associated with
SELECT
r.id,
r.client_name,
r.service_type,
r.team_id,
ut.user_id,
u.idir_email,
u.additional_email
FROM
requests as r
INNER JOIN users_teams as ut ON r.team_id=ut.team_id
INNER JOIN users as u ON u.id=ut.user_id
WHERE r.uses_team=TRUE
AND r.archived=FALSE
AND ut.pending=FALSE
ORDER BY r.client_name
-- Integration users with no team associated with
SELECT
r.id,
r.client_name,
r.user_id,
r.service_type,
u.idir_email,
u.additional_email
FROM
requests as r
INNER JOIN users as u ON u.id=r.user_id
WHERE r.uses_team=FALSE
AND r.archived=FALSE
ORDER BY r.client_name
dev
to main
and update pull request labels to choose a specific type of releaserelease:major
- will create a major release (example: v1.0.0
-> v2.0.0
)release:minor
- will create a minor release (example: v1.0.0
-> v1.1.0
)release:patch
- will create a patch release (example: v1.0.0
-> v1.0.1
)release:norelease
- will not trigger any releaseexport AWS_ECR_URI=
aws ecr get-login-password --region ca-central-1 | docker login --username AWS --password-stdin $AWS_ECR_URI
docker pull --platform linux/amd64 grafana/grafana:9.3.2
docker tag grafana/grafana:9.3.2 $AWS_ECR_URI/bcgov-sso/grafana:9.3.2
docker push $AWS_ECR_URI/bcgov-sso/grafana:9.3.2
export GF_SECURITY_ADMIN_PASSWORD=
# login to CSS app and download the prod installation json for the SSO Dashboard integration
export GF_AUTH_GENERIC_OAUTH_CLIENT_ID=
export GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET=
aws secretsmanager create-secret --name sso-grafana --description "SSO Grafana Secrets" \
--secret-string '{"GF_SECURITY_ADMIN_PASSWORD": "$GF_SECURITY_ADMIN_PASSWORD", "GF_AUTH_GENERIC_OAUTH_CLIENT_ID": "$GF_AUTH_GENERIC_OAUTH_CLIENT_ID", "GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET": "$GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET"}' --region ca-central-1
aws iam create-policy --policy-name SSOPathfinderReadGrafanaSecretInfo --policy-document ./policy.json
# navigate to secrets manager and copy the secret ARN
export GRAFANA_SECRET_ARN=
# policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "$GRAFANA_SECRET_ARN"
}
]
}
CREATE USER cssgrafana WITH PASSWORD '<secure-password>';
GRANT CONNECT ON DATABASE ssorequests TO cssgrafana;
GRANT USAGE ON SCHEMA public TO cssgrafana;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO cssgrafana;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO cssgrafana;
SELECT * from pg_catalog.pg_roles;
SELECT * FROM pg_catalog.pg_auth_members;
# check privileges on a table
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_name='requests'