Skip to main content

Oauth Token via CLI

Table of Contents

In today’s world of distributed systems and microservices, security has become more critical than ever. One common approach to securing these systems is through the use of OpenID Connect (OIDC), an identity layer built on top of the OAuth 2.0 protocol. However, integrating OIDC into command-line interface (CLI) applications can be challenging due to the complexity of OIDC flows.

In this short article there is a use-case of using oauth2c to obtain token via CLI. Keycloak is used as primary refernce of IDP, but any other OIDC complient solutions should work in the same way.

Potentially, the CLI application can be distributed to other users, so no shared secret can be used, which implictly means no Client Credentials flow.

The only safe possible approach is Authorization Code with PKCE without client secret.

Requirements:

OAuth2C is:

a command-line tool for interacting with OAuth 2.0 authorization servers. Its goal is to make it easy to fetch access tokens using any grant type or client authentication method. It is compliant with almost all basic and advanced OAuth 2.0, OIDC, OIDF FAPI and JWT profiles.

# Preparation

Oauth2c

VERSION="1.13.0"
curl -L "https://github.com/cloudentity/oauth2c/releases/download/v${VERSION}/oauth2c_${VERSION}_$(uname -s)_$(uname -m).tar.gz" | tar -xzf - oauth2c
sudo mv oauth2c /usr/bin/

OIDC client

For keycloak

  • enable “Authentication flow -> Standard flow”. For other system, use “Authorization Code” flow
  • disable “Client authentication”. For other systems, disable client secret (since it’s unsafe to store on client’s side)
  • ensure that PKCE supported (Keycloak supports it by default)
  • set allowed redirect to http://localhost:9876/callback

# Usage

Bash

ISSUER="https://<some domain>/realms/<realm name>"
CLIENT="my-cli"

TOKEN="$(oatuh2c --grant-type authorization_code \
                 --client-id "$CLIENT" \
                 --auth-method none  \
                 --pkce \
                 --response-mode query \
                 --response-types code \
                 --silent "$ISSUER" | jq .access_token)"

# use as 
#
# curl -H "Authorization: Bearer $TOKEN" ...

Python

import requests
import json

def get_token(client_id:str, issuer_url:str):
    return json.loads(
        run(
            [
                "oauth2c",
                "--grant-type",
                "authorization_code",
                "--client-id",
                client_id,
                "--auth-method",
                "none",
                "--pkce",
                "--response-mode",
                "query",
                "--response-types",
                "code",
                "--silent",
                issuer_url,
            ],
            check=True,
            capture_output=True,
        ).stdout.strip()
    )["access_token"]

# example for Keycloack
token = get_token("my-cli", "https://<some domain>/realms/<realm name>")
# use as
# requests.get("....", headers={
#     "Authorization": f"Bearer {token}"
# })