Skip to content
This repository was archived by the owner on Sep 10, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 106 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 65 additions & 0 deletions check_tokens.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env python3
import json
import base64
import subprocess
import sys

def decode_jwt(token):
"""Decode a JWT token and return its claims"""
parts = token.split('.')
if len(parts) >= 2:
payload = parts[1]
# Add padding if needed
payload += '=' * (4 - len(payload) % 4)
decoded = base64.urlsafe_b64decode(payload)
return json.loads(decoded)
return None

# Get credentials from keyring using the keyring command
try:
import keyring
entry = keyring.get_password('ftl-cli', 'default')

if not entry:
print("No credentials found in keyring")
sys.exit(1)

creds = json.loads(entry)

print("=== CHECKING BOTH TOKENS ===\n")

# Check access token
if 'access_token' in creds:
print("1. ACCESS TOKEN Claims:")
claims = decode_jwt(creds['access_token'])
print(json.dumps(claims, indent=2))
print(f"\n Has org_id? {'✅' if 'org_id' in claims else '❌'}")
print(f" Has custom claims? {'✅' if any(k not in ['iss', 'aud', 'sub', 'exp', 'iat', 'jti', 'sid'] for k in claims.keys()) else '❌'}")

print("\n" + "="*50 + "\n")

# Check ID token
if 'id_token' in creds:
print("2. ID TOKEN Claims:")
claims = decode_jwt(creds['id_token'])
print(json.dumps(claims, indent=2))
print(f"\n Has org_id? {'✅' if 'org_id' in claims else '❌'}")
print(f" Has custom claims? {'✅' if any(k not in ['iss', 'aud', 'sub', 'exp', 'iat', 'jti', 'sid', 'nonce'] for k in claims.keys()) else '❌'}")
else:
print("2. ID TOKEN: Not found in credentials")

except Exception as e:
print(f"Error: {e}")
print("\nTrying alternative method...")

# Alternative: use ftl command
try:
result = subprocess.run(["ftl", "eng", "auth", "token"], capture_output=True, text=True)
if result.returncode == 0:
token = result.stdout.strip()
print("\nAccess Token from 'ftl eng auth token':")
claims = decode_jwt(token)
print(json.dumps(claims, indent=2))
print(f"\nHas org_id? {'✅' if 'org_id' in claims else '❌'}")
except Exception as e2:
print(f"Alternative method also failed: {e2}")
32 changes: 30 additions & 2 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,12 @@ enum EngCommand {
#[arg(long, value_name = "KEY=VALUE")]
variable: Vec<String>,

/// Set access control mode (public, private)
/// Set access control mode (public, private, org, custom)
/// Overrides `FTL_ACCESS_CONTROL` env var and ftl.toml `project.access_control`
#[arg(
long = "access-control",
value_name = "public|private",
value_name = "MODE",
help = "Access control: public (no auth), private (user only), org (organization), custom (BYO auth)",
help_heading = "Authentication"
)]
access_control: Option<String>,
Expand All @@ -221,6 +222,11 @@ enum EngCommand {
#[arg(long, value_name = "URL", help_heading = "Authentication")]
jwt_issuer: Option<String>,

/// JWT audience (required when using --jwt-issuer for custom auth)
/// Overrides `FTL_JWT_AUDIENCE` env var and ftl.toml oauth.audience
#[arg(long, value_name = "AUDIENCE", help_heading = "Authentication")]
jwt_audience: Option<String>,

/// Run without making any changes (preview what would be deployed)
#[arg(long)]
dry_run: bool,
Expand Down Expand Up @@ -277,6 +283,17 @@ enum LogsOutputFormat {
enum EngAuthCommand {
/// Show authentication status
Status,
/// Manage authentication tokens
Token {
#[command(subcommand)]
command: EngAuthTokenCommand,
},
}

#[derive(Debug, Clone, Subcommand)]
enum EngAuthTokenCommand {
/// Output current user access token (for automation)
Show,
}

#[derive(Debug, Args)]
Expand Down Expand Up @@ -399,6 +416,15 @@ impl From<EngAuthCommand> for ftl_commands::auth::AuthCommand {
fn from(cmd: EngAuthCommand) -> Self {
match cmd {
EngAuthCommand::Status => Self::Status,
EngAuthCommand::Token { command } => Self::Token(command.into()),
}
}
}

impl From<EngAuthTokenCommand> for ftl_commands::auth::TokenCommand {
fn from(cmd: EngAuthTokenCommand) -> Self {
match cmd {
EngAuthTokenCommand::Show => Self::Show,
}
}
}
Expand Down Expand Up @@ -537,13 +563,15 @@ async fn handle_eng_command(args: EngArgs) -> Result<()> {
variable,
access_control,
jwt_issuer,
jwt_audience,
dry_run,
yes,
} => {
let deploy_args = ftl_commands::deploy::DeployArgs {
variables: variable,
access_control,
jwt_issuer,
jwt_audience,
dry_run,
yes,
};
Expand Down
2 changes: 1 addition & 1 deletion components/mcp-authorizer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "ftl-mcp-authorizer"
authors.workspace = true
description = "MCP authorization component for FTL servers using AuthKit"
version = "0.0.14"
version = "0.0.15-alpha.0"
license.workspace = true
rust-version.workspace = true
edition.workspace = true
Expand Down
Loading