Skip to content

Commit

Permalink
Merge pull request #5715 from govuk-one-login/AUT-3814/script-for-acc…
Browse files Browse the repository at this point in the history
…ess-token-2

AUT-3814: Script to create access token for hitting the account management API in lower envs
  • Loading branch information
mattvot authored Jan 8, 2025
2 parents e942667 + a476a14 commit 1cd9570
Show file tree
Hide file tree
Showing 9 changed files with 1,571 additions and 21 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ repos:
rev: v1.22.3
hooks:
- id: cfn-python-lint
exclude: ^(ci|.github)/.*|docker-compose.*|.pre-commit-config.yaml|orchestration-canary-alarms.template.yaml|.*.approved.json|checkov-policies.*|.terraform-docs.yml$
exclude: ^(ci|.github|http)/.*|docker-compose.*|.pre-commit-config.yaml|orchestration-canary-alarms.template.yaml|.*.approved.json|checkov-policies.*|.terraform-docs.yml|package.json|package-lock.json$
files: ^.*\.(json|yml|yaml)$

- repo: https://github.com/govuk-one-login/pre-commit-hooks.git
Expand Down
30 changes: 15 additions & 15 deletions http/account-management.http
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Please read guidance on confluence before use
# (https://govukverify.atlassian.net/wiki/spaces/LO/pages/4606132332/Manually+Testing+Account+Management+API).
# Populate these values before use. Only do manual testing in lower environments (sandpit, authdev1, authdev2).
@base-url =
@bearer-token =
# Populate the authToken value below use (using scripts/generate-auth-token.sh)

@authToken = unset

### Post to /authenticate
POST {{base-url}}/authenticate
Authorization:Bearer {{bearer-token}}
POST {{baseUrl}}/authenticate
Authorization:Bearer {{authToken}}

{
"email": "[email protected]",
Expand All @@ -19,8 +19,8 @@ Authorization:Bearer {{bearer-token}}
# notificationType must be "VERIFY_EMAIL" or "VERIFY_PHONE_NUMBER", depending desired type of OTP and subsequent
# endpoint you want to make a request to.
# The email or phoneNumber field must be populated with a new value if looking to update email or phone number.
POST {{base-url}}/send-otp-notification
Authorization:Bearer {{bearer-token}}
POST {{baseUrl}}/send-otp-notification
Authorization:Bearer {{authToken}}

{
"notificationType": "{ VERIFY_PHONE_NUMBER or VERIFY_EMAIL }",
Expand All @@ -30,8 +30,8 @@ Authorization:Bearer {{bearer-token}}

### Post to /update-email
# REQUIRES CLIENT REGISTRY CHANGE - CHANGE BACK AFTER USE
POST {{base-url}}/update-email
Authorization:Bearer {{bearer-token}}
POST {{baseUrl}}/update-email
Authorization:Bearer {{authToken}}

{
"existingEmailAddress": "[email protected]",
Expand All @@ -41,17 +41,17 @@ Authorization:Bearer {{bearer-token}}

### Post to /delete-account
# REQUIRES CLIENT REGISTRY CHANGE - CHANGE BACK AFTER USE
POST {{base-url}}/delete-account
Authorization:Bearer {{bearer-token}}
POST {{baseUrl}}/delete-account
Authorization:Bearer {{authToken}}

{
"email": "[email protected]"
}

### Post to /update-password
# REQUIRES CLIENT REGISTRY CHANGE - CHANGE BACK AFTER USE
POST {{base-url}}/update-password
Authorization:Bearer {{bearer-token}}
POST {{baseUrl}}/update-password
Authorization:Bearer {{authToken}}

{
"email": "[email protected]",
Expand All @@ -62,8 +62,8 @@ Authorization:Bearer {{bearer-token}}
# REQUIRES CLIENT REGISTRY CHANGE - CHANGE BACK AFTER USE
# After updating a phone number, when receiving an OTP going through a sign in journey immediately after, the check
# your phone screen will display the old phone number digits. This does not happen in the real service.
POST {{base-url}}/update-phone-number
Authorization:Bearer {{bearer-token}}
POST {{baseUrl}}/update-phone-number
Authorization:Bearer {{authToken}}

{
"email": "[email protected]",
Expand Down
17 changes: 17 additions & 0 deletions http/http-client.env.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"authdev1": {
"baseUrl": "https://5yss7o4p10.execute-api.eu-west-2.amazonaws.com/authdev1"
},
"authdev2": {
"baseUrl": "https://telsb8f0x9.execute-api.eu-west-2.amazonaws.com/authdev2"
},
"sandpit": {
"baseUrl": "https://ywced8o0g6.execute-api.eu-west-2.amazonaws.com/sandpit"
},
"dev": {
"baseUrl": "https://91ttse4tee.execute-api.eu-west-2.amazonaws.com/dev"
},
"build": {
"baseUrl": "https://hh097h9q9l.execute-api.eu-west-2.amazonaws.com/build"
}
}
75 changes: 75 additions & 0 deletions scripts/_generate-auth-token.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env node

const { KMSClient, SignCommand } = require("@aws-sdk/client-kms");
const asn1 = require("asn1.js");
const { v4: uuidv4 } = require("uuid");

const kmsClient = new KMSClient({ region: "eu-west-2" });

const INTERNAL_COMMON_SUBJECT_ID = process.argv[2];
const CLIENT_ID = process.argv[3];
const KEY_ID = process.argv[4];

async function signWithKMS(message) {
const command = new SignCommand({
KeyId: KEY_ID,
Message: message,
MessageType: "RAW",
SigningAlgorithm: "ECDSA_SHA_256",
});

const response = await kmsClient.send(command);
return Buffer.from(response.Signature);
}

function derToRs(derSignature) {
// ASN.1 DER decoder for ECDSA signature
const EcdsaSigAsnParse = asn1.define("EcdsaSig", function () {
this.seq().obj(this.key("r").int(), this.key("s").int());
});

const { r, s } = EcdsaSigAsnParse.decode(derSignature, "der");

const uintLength = 32; // For P-256 curve, r and s are 32 bytes each
return Buffer.concat([
r.toArrayLike(Buffer, "be", uintLength),
s.toArrayLike(Buffer, "be", uintLength),
]);
}

async function createJwt() {
const issuedTimestamp = Math.floor(new Date().valueOf() / 1000);
const payload = {
sub: INTERNAL_COMMON_SUBJECT_ID,
scope: ["openid", "email", "phone", "am"],
iss: "https://example.com",
exp: issuedTimestamp + 60 * 60 * 12,
iat: issuedTimestamp,
client_id: CLIENT_ID,
jti: uuidv4(),
};

console.log("JWT Payload", payload);

const protectedHeader = {
alg: "ES256",
kid: KEY_ID,
};

const encodedHeader = Buffer.from(JSON.stringify(protectedHeader)).toString(
"base64url",
);
const encodedPayload = Buffer.from(JSON.stringify(payload)).toString(
"base64url",
);
const signingInput = `${encodedHeader}.${encodedPayload}`;

const derSignature = await signWithKMS(Buffer.from(signingInput));
const rsSignature = derToRs(derSignature);

return `${signingInput}.${Buffer.from(rsSignature).toString("base64url")}`;
}

createJwt()
.then((jwt) => console.log("Auth Token", jwt))
.catch((err) => console.error("Error creating JWT:", err));
12 changes: 7 additions & 5 deletions scripts/export-ics.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ fi

export AWS_REGION=eu-west-2

sector="identity.$2.account.gov.uk"
if [[ $2 == authdev* ]]; then
sector="identity.$2.sandpit.account.gov.uk"
else
sector="identity.$2.account.gov.uk"
fi

echo -e "Exporting internalCommonSubjectId for Email = $1 Environment = $2 Sector = ${sector}"

Expand All @@ -31,11 +35,9 @@ up="$(

if [ -n "${up}" ]; then
ics="$(echo -n "${up}" | jq -r '.Item.SubjectID.S')"
salt="$(echo -n "${up}" | jq -r '.Item.salt.B' | base64 -d)"
digest="$(echo -n "${sector}${ics}${salt}" | openssl dgst -sha256 -binary | base64 | tr '/+' '_-' | tr -d '=')"
pwid="urn:fdc:gov.uk:2022:${digest}"
salt="$(echo -n "${up}" | jq -r '.Item.salt.B')"

echo "${pwid}"
node -e "const { calculatePairwiseIdentifier } = require('./utils'); console.log(calculatePairwiseIdentifier('${ics}', '${sector}', '${salt}'))"
else
echo "Email address $1 does not exist in $2 environment"
fi
88 changes: 88 additions & 0 deletions scripts/generate-auth-token.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/bin/bash
set -euo pipefail

environments=("authdev1" "authdev2" "sandpit" "dev" "build")

function usage() {
cat <<- USAGE
Creates an access token that can be used for direct account management API queries. Expires in 12 hours.
Usage:
$0 <environment> <email address>
Options:
-h, --help display this help message.
Arguments:
environment the environment to target to. Valid environments are: ${environments[*]}
email address the email address to create an access token for
USAGE
}

POSITIONAL=()
ENVIRONMENT=""

while (($#)); do
case $1 in
-h | --help)
usage
exit 0
;;
-*)
echo "Error: Unknown option: $1"
usage
exit 1
;;

*) POSITIONAL+=("$1") ;;
esac
shift
done

if [[ ${#POSITIONAL[@]} -ne 2 ]]; then
echo "Error: need just an environment and email address provided"
exit 1
fi

ENVIRONMENT="${POSITIONAL[0]}"
EMAIL="${POSITIONAL[1]}"

if [[ ${ENVIRONMENT} == "sandpit" ]]; then
export AWS_PROFILE="gds-di-development-admin"
KMS_KEY_ARN="arn:aws:kms:eu-west-2:761723964695:alias/sandpit-id-token-signing-key-alias"
CLIENT_ID="1Dlz5rYheTqzZASRMmSBtgFIYgZlysnQ"
elif [[ ${ENVIRONMENT} == "authdev1" ]]; then
export AWS_PROFILE="di-auth-development-admin"
KMS_KEY_ARN="arn:aws:kms:eu-west-2:653994557586:alias/authdev1-id-token-signing-key-alias"
CLIENT_ID="skwdHH2y6ERjJWTPSoAFbSt8lX04OgtI"
elif [[ ${ENVIRONMENT} == "authdev2" ]]; then
export AWS_PROFILE="di-auth-development-admin"
KMS_KEY_ARN="arn:aws:kms:eu-west-2:653994557586:alias/authdev2-id-token-signing-key-alias"
CLIENT_ID="rPEUe0hRrHqf0i0es1gYjKxE5ceGN7VK"
elif [[ ${ENVIRONMENT} == "dev" ]]; then
export AWS_PROFILE="di-auth-development-admin"
KMS_KEY_ARN="arn:aws:kms:eu-west-2:653994557586:alias/dev-id-token-signing-key-alias"
CLIENT_ID="J3tedNRsfssnsf4STuc2NNIV1C1gdxBB"
elif [[ ${ENVIRONMENT} == "build" ]]; then
export AWS_PROFILE="gds-di-development-admin"
KMS_KEY_ARN="arn:aws:kms:eu-west-2:761723964695:alias/build-id-token-signing-key-alias"
CLIENT_ID="P5OQvWV21U0OW7U5g27d6MU2LLznYYaM"
fi

configured_region="$(aws configure get region --profile "${AWS_PROFILE}" 2> /dev/null || true)"
export AWS_REGION="${configured_region:-eu-west-2}"

# shellcheck source=scripts/export_aws_creds.sh
source "./export_aws_creds.sh"

INTERNAL_COMMON_SUBJECT_ID=$(./export-ics.sh "${EMAIL}" "${ENVIRONMENT}" | tail -n 1)
if [[ ${INTERNAL_COMMON_SUBJECT_ID} == *"does not exist"* ]]; then
echo "${INTERNAL_COMMON_SUBJECT_ID}"
exit 1
fi

echo "Checking npm dependencies"
npm install --no-audit --no-fund
echo

./_generate-auth-token.js "${INTERNAL_COMMON_SUBJECT_ID}" "${CLIENT_ID}" "${KMS_KEY_ARN}"
Loading

0 comments on commit 1cd9570

Please sign in to comment.