Project: Athenz (Provisioning / Key Management)
File: Auth0.md

<a id="markdown-setup-auth0-as-athenz-oauth-idp" name="setup-auth0-as-athenz-oauth-idp"></a> <!-- TOC --> - - - - - <!-- /TOC --> <a id="markdown-target" name="target"></a> Use Github for user management. Get access token from Auth0. Authorize access token issued by Auth0 to access ZMS API OAuth2 Terminology | Terminology | Actual Party | ||| | Resource owner | Github account | | Authorization server | Auth0 | | Client | My-Athenz-SPA | | Resource server | Athenz-ZMS-API | <a id="markdown-setup-auth0" name="setup-auth0"></a> 1. copy your domain (e.g. `athenz-oauth-example.auth0.com`) Connect to Github. Create an application in auth0. sample configuration ``` Name: My-Athenz-SPA Application Type: Single Page Web Applications Allowed Callback URLs: http://localhost:3000 Allowed Web Origins: http://localhost:3000 ``` make sure to click `SAVE CHANGES` button at the bottom copy `Client ID` in `Settings` (e.g. `hpnaS7d6NmBHx4QdejenfY4kgx4RdTPH`) make sure `Connections > github` is `ON` Setup an API in auth0. sample configuration ``` Name: Athenz-ZMS-API Identifier: https://zms.athenz.io ``` <a id="markdown-setup-custom-claims" name="setup-custom-claims"></a> add `{ "Key": "CERTTHUMB", "Value": "<certificatethumbprint>" }` For the specification of `<certificate_thumbprint>`, please refer to Note: Auth0 does not support certificate bound access token currently. To support it, you need to manually manage and inject your client's certificate thumbprint. To simplify the setup, we are going to disable the certificate thumbprint verification in Athenz ZMS server. ```js // sample rules function (user, context, callback) { const namespace = 'https://myapp.example.com/'; const req = context.request; // 1. append request scopes // Get requested scopes let scopes = (req.query && req.query.scope) || (req.body && req.body.scope); // Normalize scopes into an array scopes = (scopes && scopes.split(" ")) || []; // append context.accessToken.scope = scopes; // 2. add client ID // azp === client_id context.accessToken[namespace + 'client_id'] = context.clientID; // 3. [optional] inject certificate thumbprint // const CERTTHUMB = configuration.CERTTHUMB; // context.accessToken[namespace + 'cnf'] = { 'x5t#S256': CERT_THUMB }; callback(null, user, context); } ``` <a id="markdown-setup-athenz-authority" name="setup-athenz-authority"></a> Reference: build `athenzauthauth0-*.jar` and move it to `"${DOCKER_DIR}/jars/"` add a mapping to authorize your application registered in Auth0 to access the ZMS ```bash CLIENT_ID='hpnaS7d6NmBHx4QdejenfY4kgx4RdTPH' DOMAIN='testing-domain' SERVICE='My-Athenz-SPA' PRINCIPAL="$(echo "${DOMAIN}.${SERVICE}" | tr '[:upper:]' '[:lower:]')" cat >> "${DOCKERDIR}/zms/conf/authorizedclient_ids.txt" <<EOF ${CLIENT_ID}:${PRINCIPAL}:${PRINCIPAL} EOF cat "${DOCKERDIR}/zms/conf/authorizedclient_ids.txt" ``` append the following sample properties to (Update the following `https://athenz-oauth-example.auth0.com/` domain to your own domain) ```properties athenz.auth.oauth.jwt.authnchallengerealm=registered_users@athenz.io athenz.auth.oauth.jwt.cert.excluderolecertificates=false athenz.auth.oauth.jwt.cert.excluded_principals= athenz.auth.oauth.jwt.verifycertthumbprint=false athenz.auth.oauth.jwt.authorizedclientidspath=conf/zmsserver/authorizedclientids.txt athenz.auth.oauth.jwt.claim.iss=https://athenz-oauth-example.auth0.com/ athenz.auth.oauth.jwt.claim.aud=https://zms.athenz.io athenz.auth.oauth.jwt.claim.scope=openid https://zms.athenz.io/zms/v1 athenz.auth.oauth.jwt.parserfactoryclass=com.yahoo.athenz.auth.oauth.auth0.Auth0JwtParserFactory athenz.auth.oauth.jwt.parser.jwks_url=https://athenz-oauth-example.auth0.com/.well-known/jwks.json athenz.auth.oauth.jwt.auth0.claimclientid=https://athenz-oauth-example.auth0.com/client_id athenz.auth.oauth.jwt.auth0.claim_confirm=https://athenz-oauth-example.auth0.com/cnf ``` update the `DOMAIN_ADMIN` value in to your github user ID If you are using `DEV` deployment, please update the `DEVDOMAINADMIN` value in re-deploy ZMS <a id="markdown-get-access-token" name="get-access-token"></a> For `code_challenge`, please refer to . sample values ``` verifier: LQoKwSy9djyuHHqr6h3yOYaEA9Mq9uap_u8mXpk1fBM challenge: soLgG9cEekJtNh23GxQ1hB4AbqKjcVrkOTWVXMqiUMY ``` Create request URL to get the authorization code. ```bash DOMAIN='athenz-oauth-example.auth0.com' CLIENT_ID='hpnaS7d6NmBHx4QdejenfY4kgx4RdTPH' REDIRECT_URI='http%3A%2F%2Flocalhost%3A3000' API_AUDIENCE='https%3A%2F%2Fzms.athenz.io' SCOPE='openid%20https%3A%2F%2Fzms.athenz.io%2Fzms%2Fv1' # openid https://zms.athenz.io/zms/v1 CODE_CHALLENGE='soLgG9cEekJtNh23GxQ1hB4AbqKjcVrkOTWVXMqiUMY' tr -d '[:space:]' << CURL_EOF; echo ''; https://${DOMAIN}/authorize? audience=${API_AUDIENCE}& scope=${SCOPE}& response_type=code& clientid=${CLIENTID}& codechallenge=${CODECHALLENGE}& codechallengemethod=S256& redirecturi=${REDIRECTURI} CURL_EOF ``` ```bash https://athenz-oauth-example.auth0.com/authorize?audience=athenz%2Fzms&scope=openid%20zms%2Fv1&responsetype=code&clientid=hpnaS7d6NmBHx4QdejenfY4kgx4RdTPH&codechallenge=soLgG9cEekJtNh23GxQ1hB4AbqKjcVrkOTWVXMqiUMY&codechallengemethod=S256&redirecturi=http%3A%2F%2Flocalhost%3A3000 ``` Open your browser in incognito mode (prevent messing up the cookies). Paste the URL. The `Sign In with Auth0` page should show up. Choose `LOG IN WITH GITHUB` and login with the connected Github account. Authorize the app by clicking the `YES` button. The browser will be redirected to the callback URL. Showing error? Don't panic. It is normal for the browser to show error like `ERRCONNECTIONREFUSED`, as we do not have a server to handle the callback. Check the browser's address bar. It should have the callback URL with the authorization code (e.g. `http://localhost:3000/?code=NJDuaMpm1R4A6vL4`). Copy the authorization code at the end of the redirected URL (e.g. `NJDuaMpm1R4A6vL4`). Update the authorization code in below code block and execute the command to exchange for the access token. (duplicated env. variables are

```bash YOURGENERATEDCODEVERIFIER='LQoKwSy9djyuHHqr6h3yOYaEA9Mq9uapu8mXpk1fBM' YOURAUTHORIZATIONCODE='NJDuaMpm1R4A6vL4' curl --request POST \ --url "https://${DOMAIN}/oauth/token" \ --header 'content-type: application/x-www-form-urlencoded' \ --data granttype=authorizationcode \ --data "clientid=${CLIENTID}" \ --data "codeverifier=${YOURGENERATEDCODEVERIFIER}" \ --data "code=${YOURAUTHORIZATIONCODE}" \ --data "redirecturi=${REDIRECTURI}"; echo ''; ``` ```bash { "access_token": "...", "scope": "openid https://zms.athenz.io/zms/v1", "expires_in": 86400, "token_type": "Bearer" } ``` You can check the JWT and its claims in To get the JWKS to verify your JWT, `curl "https://${DOMAIN}/.well-known/jwks.json"; echo '';` <a id="markdown-verify-access-token-against-zms" name="verify-access-token-against-zms"></a> Start testing ENV. start up testing container ```bash BASE_DIR="$(git rev-parse --show-toplevel)" . "${BASE_DIR}/docker/env.sh" docker run --rm -it --network="${DOCKERNETWORK}" -v "${BASEDIR}:/athenz" --user "$(id -u):$(id -g)" athenz-setup-env sh ``` setup testing container ```bash BASE_DIR="$(git rev-parse --show-toplevel)" . "${BASE_DIR}/docker/env.sh" echo "Done loading ENV. from ${BASE_DIR}/docker/env.sh" if [ -f "${DOCKER_DIR}/setup-scripts/dev-env-exports.sh" ]; then . "${DOCKER_DIR}/setup-scripts/dev-env-exports.sh" echo 'NOTE: You are using the DEV settings in dev-env-exports.sh !!!' fi WORKSPACEDIR="${DOCKERDIR}/sample/workspace" mkdir -p "${WORKSPACEDIR}"; cd "${WORKSPACEDIR}" cp "${DOCKERDIR}/sample/oauth/config.cnf" "${WORKSPACEDIR}" alias admincurl="curl --cacert ${ATHENZCAPATH} --key ${DOMAINADMINCERTKEYPATH} --cert ${DOMAINADMINCERTPATH}" DOMAIN='testing-domain' SERVICE='My-Athenz-SPA' SERVICE_LOWER="$(echo "${SERVICE}" | tr '[:upper:]' '[:lower:]')" PRINCIPAL="$(echo "${DOMAIN}.${SERVICE}" | tr '[:upper:]' '[:lower:]')" KEYID='testpublic_key' ``` Get service certificate from ZTS prepare CSR ```bash DOMAIN="${DOMAIN}" SERVICE="${SERVICE_LOWER}" openssl req -nodes \ -newkey rsa:2048 \ -keyout "${WORKSPACE_DIR}/key.pem" \ -out "${WORKSPACE_DIR}/csr.pem" \ -config "${WORKSPACEDIR}/config.cnf" -reqexts serviceext openssl req -text -in "${WORKSPACE_DIR}/csr.pem" | grep -e 'Subject:' -e 'DNS:' openssl rsa -pubout -in "${WORKSPACEDIR}/key.pem" -out "${WORKSPACEDIR}/public.pem" ``` register service to ZMS ```bash admin_curl --request POST \ --url "https://${ZMSHOST}:${ZMSPORT}/zms/v1/domain" \ --header 'content-type: application/json' \ --data '{"name": "'"${DOMAIN}"'","adminUsers": ["'"${DOMAIN_ADMIN}"'"]}' PUBLICKEY="$(base64 -w 0 "${WORKSPACEDIR}/public.pem" | tr '\+\=\/' '\.\-\_'; echo '';)" admin_curl --request PUT \ --url "https://${ZMSHOST}:${ZMSPORT}/zms/v1/domain/${DOMAIN}/service/${SERVICE}" \ --header 'content-type: application/json' \ --data '{"name": "'"${PRINCIPAL}"'","publicKeys": [{"id": "'"${KEYID}"'","key": "'${PUBLICKEY}'"}]}' admincurl --silent --fail --show-error --request GET --url "https://${ZMSHOST}:${ZMS_PORT}/zms/v1/domain/${DOMAIN}/service/${SERVICE}" | jq ``` get service certificate from ZTS ```bash CSR="$(cat "${WORKSPACE_DIR}/csr.pem" | awk -v ORS='\\n' '1')" admin_curl --silent --fail --show-error --request POST \ --url "https://${ZTSHOST}:${ZTSPORT}/zts/v1/instance/${DOMAIN}/${SERVICE}/refresh" \ --header 'content-type: application/json' \ --data '{"csr": "'"${CSR}"'","keyId": "'"${KEY_ID}"'"}' \ | jq --raw-output '[.certificate, .caCertBundle] | join("")' > "${WORKSPACEDIR}/srccert_bundle.pem" ``` ```bash curl --silent --fail --show-error \ --cacert "${ATHENZCAPATH}" \ --key "${WORKSPACE_DIR}/key.pem" \ --cert "${WORKSPACEDIR}/srccert_bundle.pem" \ --url "https://${ZMSHOST}:${ZMSPORT}/zms/v1/principal" | jq 'del(.token)' ``` ```json { "domain": "testing-domain", "service": "my-athenz-spa" } ``` Verify the access token from Auth0 check Athenz domain admin, make sure your github ID is one of the members ```bash curl --silent --fail --show-error \ --cacert "${ATHENZCAPATH}" \ --key "${WORKSPACE_DIR}/key.pem" \ --cert "${WORKSPACEDIR}/srccert_bundle.pem" \ --url "https://${ZMSHOST}:${ZMSPORT}/zms/v1/domain/sys.auth/role/admin" | jq ``` ```json { "name": "sys.auth:role.admin", "modified": "2020-03-05T05:34:16.498Z", "members": [ "user.github-7654321" ], "roleMembers": [ { "memberName": "user.github-7654321", "approved": true, "auditRef": "System Setup" } ] } ``` verify the access token, make sure your github ID is shown ```bash accesstoken='<encodedjwt>' curl --silent --fail --show-error \ -H "Authorization: Bearer ${access_token}" \ --cacert "${ATHENZCAPATH}" \ --key "${WORKSPACE_DIR}/key.pem" \ --cert "${WORKSPACEDIR}/srccert_bundle.pem" \ --url "https://${ZMSHOST}:${ZMSPORT}/zms/v1/principal" | jq 'del(.token)' ``` ```json { "domain": "user", "service": "github-7654321" } ``` verify admin access right of the access token ```bash curl --silent --fail --show-error \ -H "Authorization: Bearer ${access_token}" \ --cacert "${ATHENZCAPATH}" \ --key "${WORKSPACE_DIR}/key.pem" \ --cert "${WORKSPACEDIR}/srccert_bundle.pem" \ "https://${ZMSHOST}:${ZMSPORT}/zms/v1/access/some-action/sys.auth:some-resources" | jq ``` ```json { "granted": true } ``` <a id="markdown-note" name="note"></a> <a id="markdown-about-authorized-service" name="about-authorized-service"></a> ZMS limits the API access using the file (GET APIs are not included.). When a client application access ZMS using the user's credentials, the application can only use the user's credentials on a pre-defined set of APIs. Hence, even the user is the domain admin of Athenz, he cannot update ZMS data via unregistered client applications. For example, if we want to allow users to create top level domain via our example application ('testing-domain.my-athenz-spa'), we need to update the as below and re-deploy ZMS. ```json { "services" : { "testing-domain.my-athenz-spa": { "allowedOperations": [ { "name":"posttopleveldomain" } ] } } } ``` <a id="markdown-reference" name="reference"></a>