From 51502af218984d931a515bcb519a1f59308f94fd Mon Sep 17 00:00:00 2001 From: Bethuel Date: Fri, 2 Jun 2023 18:34:36 +0300 Subject: [PATCH] Support IDP manager configuration with configure.sh (#843) support IDP management configuration using configure.sh script Add initial Zitadel configuration script --- .../workflows/test-docker-compose-linux.yml | 15 ++- infrastructure_files/configure.sh | 127 ++++++++++-------- infrastructure_files/management.json.tmpl | 10 +- infrastructure_files/setup.env.example | 5 + infrastructure_files/tests/setup.env | 5 +- infrastructure_files/zitadel.sh | 122 +++++++++++++++++ management/server/idp/idp.go | 10 +- 7 files changed, 231 insertions(+), 63 deletions(-) create mode 100644 infrastructure_files/zitadel.sh diff --git a/.github/workflows/test-docker-compose-linux.yml b/.github/workflows/test-docker-compose-linux.yml index a2281b357..4dd879ee7 100644 --- a/.github/workflows/test-docker-compose-linux.yml +++ b/.github/workflows/test-docker-compose-linux.yml @@ -6,6 +6,7 @@ on: - main pull_request: + concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || github.actor_id }} cancel-in-progress: true @@ -48,6 +49,9 @@ jobs: CI_NETBIRD_AUTH_AUDIENCE: testing.ci CI_NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT: https://example.eu.auth0.com/.well-known/openid-configuration CI_NETBIRD_USE_AUTH0: true + CI_NETBIRD_MGMT_IDP: "none" + CI_NETBIRD_IDP_MGMT_CLIENT_ID: testing.client.id + CI_NETBIRD_IDP_MGMT_CLIENT_SECRET: testing.client.secret - name: check values working-directory: infrastructure_files @@ -67,6 +71,9 @@ jobs: CI_NETBIRD_AUTH_USER_ID_CLAIM: "email" CI_NETBIRD_AUTH_DEVICE_AUTH_AUDIENCE: "super" CI_NETBIRD_AUTH_DEVICE_AUTH_SCOPE: "openid email" + CI_NETBIRD_MGMT_IDP: "none" + CI_NETBIRD_IDP_MGMT_CLIENT_ID: testing.client.id + CI_NETBIRD_IDP_MGMT_CLIENT_SECRET: testing.client.secret run: | grep AUTH_CLIENT_ID docker-compose.yml | grep $CI_NETBIRD_AUTH_CLIENT_ID @@ -83,10 +90,16 @@ jobs: grep -A 1 ProviderConfig management.json | grep Audience | grep $CI_NETBIRD_AUTH_DEVICE_AUTH_AUDIENCE grep Scope management.json | grep "$CI_NETBIRD_AUTH_DEVICE_AUTH_SCOPE" grep UseIDToken management.json | grep false + grep -A 1 IdpManagerConfig management.json | grep ManagerType | grep $CI_NETBIRD_MGMT_IDP + grep -A 3 IdpManagerConfig management.json | grep -A 1 ClientConfig | grep Issuer | grep $CI_NETBIRD_AUTH_AUTHORITY + grep -A 4 IdpManagerConfig management.json | grep -A 2 ClientConfig | grep TokenEndpoint | grep $CI_NETBIRD_AUTH_TOKEN_ENDPOINT + grep -A 5 IdpManagerConfig management.json | grep -A 3 ClientConfig | grep ClientID | grep $CI_NETBIRD_IDP_MGMT_CLIENT_ID + grep -A 6 IdpManagerConfig management.json | grep -A 4 ClientConfig | grep ClientSecret | grep $CI_NETBIRD_IDP_MGMT_CLIENT_SECRET + grep -A 7 IdpManagerConfig management.json | grep -A 5 ClientConfig | grep GrantType | grep client_credentials - name: run docker compose up working-directory: infrastructure_files - run: | + run: | docker-compose up -d sleep 5 docker-compose ps diff --git a/infrastructure_files/configure.sh b/infrastructure_files/configure.sh index 872d46c4b..663baa7da 100755 --- a/infrastructure_files/configure.sh +++ b/infrastructure_files/configure.sh @@ -1,34 +1,29 @@ #!/bin/bash -if ! which curl > /dev/null 2>&1 -then - echo "This script uses curl fetch OpenID configuration from IDP." - echo "Please install curl and re-run the script https://curl.se/" - echo "" - exit 1 +if ! which curl >/dev/null 2>&1; then + echo "This script uses curl fetch OpenID configuration from IDP." + echo "Please install curl and re-run the script https://curl.se/" + echo "" + exit 1 fi -if ! which jq > /dev/null 2>&1 -then - echo "This script uses jq to load OpenID configuration from IDP." - echo "Please install jq and re-run the script https://stedolan.github.io/jq/" - echo "" - exit 1 +if ! which jq >/dev/null 2>&1; then + echo "This script uses jq to load OpenID configuration from IDP." + echo "Please install jq and re-run the script https://stedolan.github.io/jq/" + echo "" + exit 1 fi source setup.env source base.setup.env -if ! which envsubst > /dev/null 2>&1 -then +if ! which envsubst >/dev/null 2>&1; then echo "envsubst is needed to run this script" - if [[ $(uname) == "Darwin" ]] - then + if [[ $(uname) == "Darwin" ]]; then echo "you can install it with homebrew (https://brew.sh):" echo "brew install gettext" else - if which apt-get > /dev/null 2>&1 - then + if which apt-get >/dev/null 2>&1; then echo "you can install it by running" echo "apt-get update && apt-get install gettext-base" else @@ -38,8 +33,7 @@ then exit 1 fi -if [[ "x-$NETBIRD_DOMAIN" == "x-" ]] -then +if [[ "x-$NETBIRD_DOMAIN" == "x-" ]]; then echo NETBIRD_DOMAIN is not set, please update your setup.env file echo If you are migrating from old versions, you migh need to update your variables prefixes from echo WIRETRUSTEE_.. TO NETBIRD_ @@ -47,8 +41,7 @@ then fi # local development or tests -if [[ $NETBIRD_DOMAIN == "localhost" || $NETBIRD_DOMAIN == "127.0.0.1" ]] -then +if [[ $NETBIRD_DOMAIN == "localhost" || $NETBIRD_DOMAIN == "127.0.0.1" ]]; then export NETBIRD_MGMT_SINGLE_ACCOUNT_MODE_DOMAIN="netbird.selfhosted" export NETBIRD_MGMT_API_ENDPOINT=http://$NETBIRD_DOMAIN:$NETBIRD_MGMT_API_PORT unset NETBIRD_MGMT_API_CERT_FILE @@ -56,9 +49,8 @@ then fi # if not provided, we generate a turn password -if [[ "x-$TURN_PASSWORD" == "x-" ]] -then - export TURN_PASSWORD=$(openssl rand -base64 32|sed 's/=//g') +if [[ "x-$TURN_PASSWORD" == "x-" ]]; then + export TURN_PASSWORD=$(openssl rand -base64 32 | sed 's/=//g') fi MGMT_VOLUMENAME="${VOLUME_PREFIX}${MGMT_VOLUMESUFFIX}" @@ -67,13 +59,13 @@ LETSENCRYPT_VOLUMENAME="${VOLUME_PREFIX}${LETSENCRYPT_VOLUMESUFFIX}" # if volume with wiretrustee- prefix already exists, use it, else create new with netbird- OLD_PREFIX='wiretrustee-' if docker volume ls | grep -q "${OLD_PREFIX}${MGMT_VOLUMESUFFIX}"; then - MGMT_VOLUMENAME="${OLD_PREFIX}${MGMT_VOLUMESUFFIX}" + MGMT_VOLUMENAME="${OLD_PREFIX}${MGMT_VOLUMESUFFIX}" fi if docker volume ls | grep -q "${OLD_PREFIX}${SIGNAL_VOLUMESUFFIX}"; then - SIGNAL_VOLUMENAME="${OLD_PREFIX}${SIGNAL_VOLUMESUFFIX}" + SIGNAL_VOLUMENAME="${OLD_PREFIX}${SIGNAL_VOLUMESUFFIX}" fi if docker volume ls | grep -q "${OLD_PREFIX}${LETSENCRYPT_VOLUMESUFFIX}"; then - LETSENCRYPT_VOLUMENAME="${OLD_PREFIX}${LETSENCRYPT_VOLUMESUFFIX}" + LETSENCRYPT_VOLUMENAME="${OLD_PREFIX}${LETSENCRYPT_VOLUMESUFFIX}" fi export MGMT_VOLUMENAME @@ -83,47 +75,45 @@ export LETSENCRYPT_VOLUMENAME #backwards compatibility after migrating to generic OIDC with Auth0 if [[ -z "${NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT}" ]]; then - if [[ -z "${NETBIRD_AUTH0_DOMAIN}" ]]; then - # not a backward compatible state - echo "NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT property must be set in the setup.env file" - exit 1 - fi + if [[ -z "${NETBIRD_AUTH0_DOMAIN}" ]]; then + # not a backward compatible state + echo "NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT property must be set in the setup.env file" + exit 1 + fi - echo "It seems like you provided an old setup.env file." - echo "Since the release of v0.8.10, we introduced a new set of properties." - echo "The script is backward compatible and will continue automatically." - echo "In the future versions it will be deprecated. Please refer to the documentation to learn about the changes http://netbird.io/docs/getting-started/self-hosting" + echo "It seems like you provided an old setup.env file." + echo "Since the release of v0.8.10, we introduced a new set of properties." + echo "The script is backward compatible and will continue automatically." + echo "In the future versions it will be deprecated. Please refer to the documentation to learn about the changes http://netbird.io/docs/getting-started/self-hosting" - export NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT="https://${NETBIRD_AUTH0_DOMAIN}/.well-known/openid-configuration" - export NETBIRD_USE_AUTH0="true" - export NETBIRD_AUTH_AUDIENCE=${NETBIRD_AUTH0_AUDIENCE} - export NETBIRD_AUTH_CLIENT_ID=${NETBIRD_AUTH0_CLIENT_ID} + export NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT="https://${NETBIRD_AUTH0_DOMAIN}/.well-known/openid-configuration" + export NETBIRD_USE_AUTH0="true" + export NETBIRD_AUTH_AUDIENCE=${NETBIRD_AUTH0_AUDIENCE} + export NETBIRD_AUTH_CLIENT_ID=${NETBIRD_AUTH0_CLIENT_ID} fi echo "loading OpenID configuration from ${NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT} to the openid-configuration.json file" curl "${NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT}" -q -o openid-configuration.json -export NETBIRD_AUTH_AUTHORITY=$( jq -r '.issuer' openid-configuration.json ) -export NETBIRD_AUTH_JWT_CERTS=$( jq -r '.jwks_uri' openid-configuration.json ) -export NETBIRD_AUTH_SUPPORTED_SCOPES=$( jq -r '.scopes_supported | join(" ")' openid-configuration.json ) -export NETBIRD_AUTH_TOKEN_ENDPOINT=$( jq -r '.token_endpoint' openid-configuration.json ) -export NETBIRD_AUTH_DEVICE_AUTH_ENDPOINT=$( jq -r '.device_authorization_endpoint' openid-configuration.json ) +export NETBIRD_AUTH_AUTHORITY=$(jq -r '.issuer' openid-configuration.json) +export NETBIRD_AUTH_JWT_CERTS=$(jq -r '.jwks_uri' openid-configuration.json) +export NETBIRD_AUTH_SUPPORTED_SCOPES=$(jq -r '.scopes_supported | join(" ")' openid-configuration.json) +export NETBIRD_AUTH_TOKEN_ENDPOINT=$(jq -r '.token_endpoint' openid-configuration.json) +export NETBIRD_AUTH_DEVICE_AUTH_ENDPOINT=$(jq -r '.device_authorization_endpoint' openid-configuration.json) -if [ $NETBIRD_USE_AUTH0 == "true" ] -then - export NETBIRD_AUTH_SUPPORTED_SCOPES="openid profile email offline_access api email_verified" +if [ "$NETBIRD_USE_AUTH0" == "true" ]; then + export NETBIRD_AUTH_SUPPORTED_SCOPES="openid profile email offline_access api email_verified" else - export NETBIRD_AUTH_SUPPORTED_SCOPES="openid profile email offline_access api" + export NETBIRD_AUTH_SUPPORTED_SCOPES="openid profile email offline_access api" fi if [[ ! -z "${NETBIRD_AUTH_DEVICE_AUTH_CLIENT_ID}" ]]; then - # user enabled Device Authorization Grant feature - export NETBIRD_AUTH_DEVICE_AUTH_PROVIDER="hosted" + # user enabled Device Authorization Grant feature + export NETBIRD_AUTH_DEVICE_AUTH_PROVIDER="hosted" fi # Check if letsencrypt was disabled -if [[ "$NETBIRD_DISABLE_LETSENCRYPT" == "true" ]] -then +if [[ "$NETBIRD_DISABLE_LETSENCRYPT" == "true" ]]; then export NETBIRD_DASHBOARD_ENDPOINT="https://$NETBIRD_DOMAIN:443" export NETBIRD_SIGNAL_ENDPOINT="https://$NETBIRD_DOMAIN:$NETBIRD_SIGNAL_PORT" @@ -145,8 +135,31 @@ then unset NETBIRD_MGMT_API_CERT_KEY_FILE fi +# Check if management identity provider is set +if [ -n "$NETBIRD_MGMT_IDP" ]; then + EXTRA_CONFIG={} + + # extract extra config from all env prefixed with NETBIRD_IDP_MGMT_EXTRA_ + for var in ${!NETBIRD_IDP_MGMT_EXTRA_*}; do + # convert key snake case to camel case + key=$( + echo "${var#NETBIRD_IDP_MGMT_EXTRA_}" | awk -F "_" \ + '{for (i=1; i<=NF; i++) {output=output substr($i,1,1) tolower(substr($i,2))} print output}' + ) + value="${!var}" + + echo "$var" + EXTRA_CONFIG=$(jq --arg k "$key" --arg v "$value" '.[$k] = $v' <<<"$EXTRA_CONFIG") + done + + export NETBIRD_MGMT_IDP + export NETBIRD_IDP_MGMT_CLIENT_ID + export NETBIRD_IDP_MGMT_CLIENT_SECRET + export NETBIRD_IDP_MGMT_EXTRA_CONFIG=$EXTRA_CONFIG +fi + env | grep NETBIRD -envsubst < docker-compose.yml.tmpl > docker-compose.yml -envsubst < management.json.tmpl > management.json -envsubst < turnserver.conf.tmpl > turnserver.conf +envsubst docker-compose.yml +envsubst management.json +envsubst turnserver.conf diff --git a/infrastructure_files/management.json.tmpl b/infrastructure_files/management.json.tmpl index a0a91b3d3..c2c93a665 100644 --- a/infrastructure_files/management.json.tmpl +++ b/infrastructure_files/management.json.tmpl @@ -38,7 +38,15 @@ "OIDCConfigEndpoint":"$NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT" }, "IdpManagerConfig": { - "Manager": "none" + "ManagerType": "$NETBIRD_MGMT_IDP", + "ClientConfig": { + "Issuer": "$NETBIRD_AUTH_AUTHORITY", + "TokenEndpoint": "$NETBIRD_AUTH_TOKEN_ENDPOINT", + "ClientID": "$NETBIRD_IDP_MGMT_CLIENT_ID", + "ClientSecret": "$NETBIRD_IDP_MGMT_CLIENT_SECRET", + "GrantType": "client_credentials" + }, + "ExtraConfig": $NETBIRD_IDP_MGMT_EXTRA_CONFIG }, "DeviceAuthorizationFlow": { "Provider": "$NETBIRD_AUTH_DEVICE_AUTH_PROVIDER", diff --git a/infrastructure_files/setup.env.example b/infrastructure_files/setup.env.example index 6781e050a..391a34b3e 100644 --- a/infrastructure_files/setup.env.example +++ b/infrastructure_files/setup.env.example @@ -22,6 +22,11 @@ NETBIRD_AUTH_DEVICE_AUTH_CLIENT_ID="" NETBIRD_AUTH_DEVICE_AUTH_AUDIENCE=$NETBIRD_AUTH_AUDIENCE NETBIRD_AUTH_DEVICE_AUTH_SCOPE="openid" NETBIRD_AUTH_DEVICE_AUTH_USE_ID_TOKEN=false +eg. zitadel, auth0, azure, keycloak +NETBIRD_MGMT_IDP="none" +# Some IDPs requires different client id and client secret for management api +NETBIRD_IDP_MGMT_CLIENT_ID=$NETBIRD_AUTH_CLIENT_ID +NETBIRD_IDP_MGMT_CLIENT_SECRET="" # if your IDP provider doesn't support fragmented URIs, configure custom # redirect and silent redirect URIs, these will be concatenated into your NETBIRD_DOMAIN domain. diff --git a/infrastructure_files/tests/setup.env b/infrastructure_files/tests/setup.env index cafcba246..824831841 100644 --- a/infrastructure_files/tests/setup.env +++ b/infrastructure_files/tests/setup.env @@ -16,4 +16,7 @@ NETBIRD_DISABLE_LETSENCRYPT=true NETBIRD_TOKEN_SOURCE="idToken" NETBIRD_AUTH_DEVICE_AUTH_AUDIENCE="super" NETBIRD_AUTH_USER_ID_CLAIM="email" -NETBIRD_AUTH_DEVICE_AUTH_SCOPE="openid email" \ No newline at end of file +NETBIRD_AUTH_DEVICE_AUTH_SCOPE="openid email" +NETBIRD_MGMT_IDP=$CI_NETBIRD_MGMT_IDP +NETBIRD_IDP_MGMT_CLIENT_ID=$CI_NETBIRD_IDP_MGMT_CLIENT_ID +NETBIRD_IDP_MGMT_CLIENT_SECRET=$CI_NETBIRD_IDP_MGMT_CLIENT_SECRET \ No newline at end of file diff --git a/infrastructure_files/zitadel.sh b/infrastructure_files/zitadel.sh new file mode 100644 index 000000000..1b387a9cf --- /dev/null +++ b/infrastructure_files/zitadel.sh @@ -0,0 +1,122 @@ +#!/bin/bash + +set -e + +request_jwt_token() { + INSTANCE_URL=$1 + BODY="grant_type=client_credentials&scope=urn:zitadel:iam:org:project:id:zitadel:aud&client_id=$ZITADEL_CLIENT_ID&client_secret=$ZITADEL_CLIENT_SECRET" + + RESPONSE=$( + curl -X POST "$INSTANCE_URL/oauth/v2/token" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "$BODY" + ) + echo "$RESPONSE" | jq -r '.access_token' +} + +create_new_project() { + INSTANCE_URL=$1 + ACCESS_TOKEN=$2 + PROJECT_NAME="NETBIRD" + + RESPONSE=$( + curl -X POST "$INSTANCE_URL/management/v1/projects" \ + -H "Authorization: Bearer $ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name": "'"$PROJECT_NAME"'"}' + ) + echo "$RESPONSE" | jq -r '.id' +} + +create_new_application() { + INSTANCE_URL=$1 + ACCESS_TOKEN=$2 + APPLICATION_NAME="netbird" + + RESPONSE=$( + curl -X POST "$INSTANCE_URL/management/v1/projects/$PROJECT_ID/apps/oidc" \ + -H "Authorization: Bearer $ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "'"$APPLICATION_NAME"'", + "redirectUris": [ + "'"$BASE_REDIRECT_URL"'/auth" + ], + "RESPONSETypes": [ + "OIDC_RESPONSE_TYPE_CODE" + ], + "grantTypes": [ + "OIDC_GRANT_TYPE_AUTHORIZATION_CODE", + "OIDC_GRANT_TYPE_REFRESH_TOKEN" + ], + "appType": "OIDC_APP_TYPE_USER_AGENT", + "authMethodType": "OIDC_AUTH_METHOD_TYPE_NONE", + "postLogoutRedirectUris": [ + "'"$BASE_REDIRECT_URL"'/silent-auth" + ], + "version": "OIDC_VERSION_1_0", + "devMode": '"$ZITADEL_DEV_MODE"', + "accessTokenType": "OIDC_TOKEN_TYPE_JWT", + "accessTokenRoleAssertion": true, + "skipNativeAppSuccessPage": true + }' + ) + echo "$RESPONSE" | jq -r '.clientId' +} + +configure_zitadel_instance() { + # extract zitadel instance url + INSTANCE_URL=$(echo "$NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT" | sed 's/\/\.well-known\/openid-configuration//') + DOC_URL="https://netbird.io/docs/integrations/identity-providers/self-hosted/using-netbird-with-zitadel#step-4-create-a-service-user" + + echo "" + printf "configuring zitadel instance: $INSTANCE_URL \n \ + before proceeding, please create a new service account for authorization by following the instructions (step 4 and 5 + ) in the documentation at %s\n" "$DOC_URL" + echo "Please ensure that the new service account has 'Org Owner' permission in order for this to work." + echo "" + + read -n 1 -s -r -p "press any key to continue..." + echo "" + + # prompt the user to enter service account clientID + echo "" + read -r -p "enter service account ClientId: " ZITADEL_CLIENT_ID + echo "" + + # Prompt the user to enter service account clientSecret + read -r -p "enter service account ClientSecret: " ZITADEL_CLIENT_SECRET + echo "" + + # get an access token from zitadel + echo "retrieving access token from zitadel" + ACCESS_TOKEN=$(request_jwt_token "$INSTANCE_URL") + if [ "$ACCESS_TOKEN" = "null" ]; then + echo "failed requesting access token" + exit 1 + fi + + # create the zitadel project + echo "creating new zitadel project" + PROJECT_ID=$(create_new_project "$INSTANCE_URL" "$ACCESS_TOKEN") + if [ "$PROJECT_ID" = "null" ]; then + echo "failed creating new zitadel project" + exit 1 + fi + + ZITADEL_DEV_MODE=false + if [[ $NETBIRD_DOMAIN == *"localhost"* ]]; then + BASE_REDIRECT_URL="http://$NETBIRD_DOMAIN" + ZITADEL_DEV_MODE=true + else + BASE_REDIRECT_URL="https://$NETBIRD_DOMAIN" + fi + + # create zitadel spa application + echo "creating new zitadel spa application" + APPLICATION_CLIENT_ID=$(create_new_application "$INSTANCE_URL" "$ACCESS_TOKEN") + if [ "$APPLICATION_CLIENT_ID" = "null" ]; then + echo "failed creating new zitadel spa application" + exit 1 + fi +} diff --git a/management/server/idp/idp.go b/management/server/idp/idp.go index 95c579b94..f269e8f06 100644 --- a/management/server/idp/idp.go +++ b/management/server/idp/idp.go @@ -84,6 +84,10 @@ type JWTToken struct { // NewManager returns a new idp manager based on the configuration that it receives func NewManager(config Config, appMetrics telemetry.AppMetrics) (Manager, error) { + if config.ClientConfig != nil { + config.ClientConfig.Issuer = strings.TrimSuffix(config.ClientConfig.Issuer, "/") + } + switch strings.ToLower(config.ManagerType) { case "none", "": return nil, nil @@ -108,8 +112,8 @@ func NewManager(config Config, appMetrics telemetry.AppMetrics) (Manager, error) ClientSecret: config.ClientConfig.ClientSecret, GrantType: config.ClientConfig.GrantType, TokenEndpoint: config.ClientConfig.TokenEndpoint, - ObjectID: config.ExtraConfig["ObjectID"], - GraphAPIEndpoint: config.ExtraConfig["GraphAPIEndpoint"], + ObjectID: config.ExtraConfig["ObjectId"], + GraphAPIEndpoint: config.ExtraConfig["GraphApiEndpoint"], } } @@ -155,7 +159,7 @@ func NewManager(config Config, appMetrics telemetry.AppMetrics) (Manager, error) Issuer: config.ClientConfig.Issuer, TokenEndpoint: config.ClientConfig.TokenEndpoint, GrantType: config.ClientConfig.GrantType, - APIToken: config.ExtraConfig["APIToken"], + APIToken: config.ExtraConfig["ApiToken"], } return NewOktaManager(oktaClientConfig, appMetrics)