Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature jwt auth #938

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

Feature jwt auth #938

wants to merge 3 commits into from

Conversation

spunkedy
Copy link

@spunkedy spunkedy commented Mar 8, 2024

With a client id / secret, to be secure you will have to go through a rotation. If you do a JWT signer, it's a little bit more complex, but you can still get the client going.

here is an example with grabbing the jwt from a signed aws kms so that you aren't responsible for getting the details.

#!/bin/bash

# Variables
KMS_KEY_ARN="arn_here"
ALGORITHM="RSASSA_PKCS1_V1_5_SHA_256"
CLIENT_ID="test"
AUTH_SERVER_TOKEN_ENDPOINT_URL="http://localhost:8080/realms/master/protocol/openid-connect/token"

# Helper functions
base64url_encode() {
    openssl enc -base64 -A | tr '+/' '-_' | tr -d '='
}

generate_jti() {
    # Generates a random string for jti using /dev/urandom
    head -c 16 /dev/urandom | base64 | tr -d '/+=' | cut -c -22
}


# JWT Claims
ISSUER="$CLIENT_ID" # Issuer
SUBJECT="$CLIENT_ID" # Subject
AUDIENCE="$AUTH_SERVER_TOKEN_ENDPOINT_URL" # Audience
JTI=$(generate_jti) # JWT ID
CURRENT_TIME=$(date +%s)
EXPIRATION_TIME=$(($CURRENT_TIME + 3600)) # 1 hour from now

# Create JWT Header
HEADER=$(jq -n --arg alg "RS256" --arg typ "JWT" '{alg: $alg, typ: $typ}')
HEADER_BASE64=$(echo -n "$HEADER" | base64url_encode)

# Create JWT Payload
PAYLOAD=$(jq -n \
    --arg iss "$ISSUER" \
    --arg sub "$SUBJECT" \
    --arg aud "$AUDIENCE" \
    --arg jti "$JTI" \
    --argjson iat $CURRENT_TIME \
    --argjson exp $EXPIRATION_TIME \
    '{iss: $iss, sub: $sub, aud: $aud, jti: $jti, iat: $iat, exp: $exp}')
PAYLOAD_BASE64=$(echo -n "$PAYLOAD" | base64url_encode)

# Prepare the message to be signed
MESSAGE="$HEADER_BASE64.$PAYLOAD_BASE64"

# Use AWS KMS to sign the message
SIGNATURE_JSON=$(echo -n "$MESSAGE" | aws kms sign \
    --key-id "$KMS_KEY_ARN" \
    --message-type RAW \
    --signing-algorithm "$ALGORITHM" \
    --message fileb:///dev/stdin \
    --output json)
SIGNATURE=$(echo $SIGNATURE_JSON | jq -r '.Signature' | base64 -d | base64url_encode)

# Construct the JWT
JWT="$HEADER_BASE64.$PAYLOAD_BASE64.$SIGNATURE"

echo "jwt = \"$JWT\"" > terraform.tfvars

you can then use it like:

variable "jwt" {
  type        = string
  description = "JWT for client authorization"
}

provider "keycloak" {
  client_assertion = var.jwt
  url = "http://localhost:8080"
}

@spunkedy
Copy link
Author

spunkedy commented Mar 8, 2024

Just updated the logic since the JTI of the token isn't allowed to be reused and the existing logic retried.

Looking for feedback and I can update this, if it's better to do it a different way let me know.

@datenzar
Copy link

datenzar commented Nov 27, 2024

Hi @spunkedy,
if I understand your PR right, this can be used to authenticate the Keycloak provider using a JWT token. That would be really helpful as we run Terraform jobs in our CI pipeline which should authenticate with a short-lived token rather than rotating secrets (ideally we would use X509 certificate authentication but that would be even more complex to achieve).

@mrparkers do you see any missing steps in order to bring this PR into main?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants