Jwtex - A serverless JWT exchanger and OIDC IdP

jwtex

*This README is a work in progress

jwtex is a serverless application that takes JSON Web Tokens (JWTs) in one format and converts them to another format. It also acts as a basic OpenID Connect (OIDC) identity provider to authenticate the emitted JWTs.

Use cases

tl;dr: Create useful AWS role session tags from JWTs issued by GitHub, GitLab, etc.

GitHub Actions can generate OIDC tokens to authenticate CI/CD jobs. This can be used to federate into many systems, including AWS. The JWT that GitHub generates contains a wealth of information about the job that created it, but most of that useful information is discarded when federating into AWS. For example, here's a GitHub JWT's claims:

{
  "actor": "octocat",
  "aud": "https://github.com/octo-org",
  "base_ref": "",
  "environment": "prod",
  "event_name": "workflow_dispatch",
  "exp": 1632493867,
  "head_ref": "",
  "iat": 1632493567,
  "iss": "https://token.actions.githubusercontent.com",
  "job_workflow_ref": "octo-org/octo-automation/.github/workflows/oidc.yml@refs/heads/main",
  "jti": "example-id",
  "nbf": 1632492967,
  "ref": "refs/heads/main",
  "ref_type": "branch",
  "repository": "octo-org/octo-repo",
  "repository_owner": "octo-org",
  "run_attempt": "2",
  "run_id": "example-run-id",
  "run_number": "10",
  "sha": "example-sha",
  "sub": "repo:octo-org/octo-repo:environment:prod",
  "workflow": "example-workflow"
}

And this is what appears in CloudTrail (I've removed irrelevant fields for brevity):

{
  "eventName": "AssumeRoleWithWebIdentity",
  "eventSource": "sts.amazonaws.com",
  "recipientAccountId": "0123456789012",
  "requestParameters": {
    "roleArn": "arn:aws:iam::0123456789012:role/ExampleGithubRole",
    "roleSessionName": "botocore-session-1631674835"
  },
  "responseElements": {
    "assumedRoleUser": {
      "arn": "arn:aws:sts::0123456789012:assumed-role/ExampleGithubRole/botocore-session-1631674835",
      "assumedRoleId": "AROAY99999AOBPS6VNUFM:botocore-session-1631674835"
    },
    "audience": "https://github.com/octo-org",
    "credentials": {
      "accessKeyId": "ASIAY29999OMG3MKNAG",
      "expiration": "Sep 15, 2021 4:00:36 AM",
      "sessionToken": "IQ[trimmed]lg=="
    },
    "provider": "arn:aws:iam::0123456789012:oidc-provider/token.actions.githubusercontent.com",
    "subjectFromWebIdentityToken": "repo:octo-org/octo-repo:environment:prod"
  },
  "sourceIPAddress": "104.211.45.236",
  "userAgent": "aws-cli/2.2.35 Python/3.8.8 Linux/5.8.0-1040-azure exe/x86_64.ubuntu.20 prompt/off command/sts.get-caller-identity",
  "userIdentity": {
    "identityProvider": "arn:aws:iam::0123456789012:oidc-provider/token.actions.githubusercontent.com",
    "principalId": "arn:aws:iam::0123456789012:oidc-provider/token.actions.githubusercontent.com:https://github.com/octo-org:repo:octo-org/octo-repo:environment:prod",
    "type": "WebIdentityUser",
    "userName": "repo:octo-org/octo-repo:environment:prod"
  }
}

The only useful information that is passed through is the sub. It would be really great if we could a) record other claims of the GitHub JWT in CloudTrail and b) use those other claims as AWS IAM role session tags.

Let's make it happen

Here's how to deploy this:

  • Create a new AWS account in your org solely for running jwtex. You should minimise who has access to it as it is a sensitive service.

  • Deploy jwtex into that account:

# jwtex.yml
Transform: AWS::Serverless-2016-10-31
Resources:
  jwtex:
    Type: AWS::Serverless::Application
    Properties:
      Location:
        ApplicationId: 'arn:aws:serverlessrepo:us-east-1:607481581596:applications/jwtex'
        SemanticVersion: '0.1.0'
    Parameters:
      Prefix: jwtex # ssm parameter prefix *without* leading slash
      CertificateArn: arn:aws:acm:us-east-1:0123456789012:certificate/ae2265dc-6397-40cf-b8e4-24f890e26d2e
      DomainName: jwtex.example.com
      HostedZoneId: Z1234YD7WANM86
      MapperFunctionArn: !GetAtt Mapper.Arn

  GithubIssuer:
    Type: AWS::SSM::Parameter
    Properties:
      Name: /jwtex/issuers/github
      Type: String
      Value: '{"issuer": "https://token.actions.githubusercontent.com"}'

  Mapper:
    Type: AWS::Serverless::Function
    Properties:
      Architectures: [arm64]
      Runtime: nodejs14.x
      Handler: mapper.handler
      CodeUri: ./mapper.js
// mapper.js
module.exports.handler = async function(input) {
    const claims = input.claims;

    if (claims.repository_owner !== "octo-org") {
        return { allow: false };
    }

    // let's extract these claims from the github jwt into
    // role session tags
    const interestingClaims = [
        "actor",
        "event_name",
        "ref",
        "repository",
        "run_attempt",
        "run_id",
        "run_number",
        "sha",
        "workflow"
    ];
    
    const tags = Object.fromEntries(interestingClaims.map(name => [
        name,
        [claims[name]]
    ]));

    claims["https://aws.amazon.com/tags"] = {
        principal_tags: tags,
        transitive_tag_keys: [],
    };

    return { allow: true, claims };
}
  • Deploy your new OIDC IdP and roles for GitHub into each AWS account in the org:
Resources:
  JwtexOidc:
    Type: AWS::IAM::OIDCProvider
    Properties:
      Url: https://jwtex.example.com
      ThumbprintList: ["TODO: insert thumbprint here"]
      ClientIdList: [https://github.com/octo-org]
      
  Role:
    Type: AWS::IAM::Role
    Properties:
      RoleName: ExampleGithubRole
      ManagedPolicyArns: [arn:aws:iam::aws:policy/ReadOnlyAccess]
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Action: sts:AssumeRoleWithWebIdentity
            Principal:
              Federated: !Ref JwtexOidc
            Condition:
              StringEquals:
                aws:RequestTag/repository: octo-org/octo-repo
                aws:RequestTag/ref: refs/heads/main
          - Effect: Allow
            Action: sts:TagSession
            Principal:
              Federated: !Ref JwtexOidc
  • Update your GHA workflows to exchange your GitHub JWT for a new JWT:
on:
  push:

permissions:
  id-token: write
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Configure AWS
        run: |
          export AWS_WEB_IDENTITY_TOKEN_FILE=/tmp/awscreds
          echo AWS_WEB_IDENTITY_TOKEN_FILE=$AWS_WEB_IDENTITY_TOKEN_FILE >> $GITHUB_ENV
          
          github_jwt=$(curl -s -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r '.value')
          url="https://jwtex.example.com/exchange?issuerId=github"
          curl -s --data-binary "$github_jwt" -o $AWS_WEB_IDENTITY_TOKEN_FILE $url
          
      - run: aws sts get-caller-identity --region us-east-1
        env:
          AWS_ROLE_ARN: arn:aws:iam::0123456789012:role/ExampleGithubRole

Now your role sessions have those helpful tags and your CloudTrail entries have been enriched:

diff --git cloudtrail.json cloudtrail.json
index 649867d..2784d1a 100644
--- cloudtrail.json
+++ cloudtrail.json
@@ -3,8 +3,20 @@
   "eventSource": "sts.amazonaws.com",
   "recipientAccountId": "0123456789012",
   "requestParameters": {
+    "principalTags": {
+      "actor": "octocat",
+      "event_name": "workflow_dispatch",
+      "ref": "refs/heads/main",
+      "repository": "octo-org/octo-repo",
+      "run_attempt": "2",
+      "run_id": "example-run-id",
+      "run_number": "10",
+      "sha": "example-sha",
+      "workflow": "example-workflow"
+    },
+    "transitiveTagKeys": [],
     "roleArn": "arn:aws:iam::0123456789012:role/ExampleGithubRole",
     "roleSessionName": "botocore-session-1631674835"
   },
   "responseElements": {
     "assumedRoleUser": {
@@ -17,14 +29,15 @@
       "expiration": "Sep 15, 2021 4:00:36 AM",
       "sessionToken": "IQ[trimmed]lg=="
     },
-    "provider": "arn:aws:iam::0123456789012:oidc-provider/token.actions.githubusercontent.com",
+    "packedPolicySize": 44,
+    "provider": "arn:aws:iam::0123456789012:oidc-provider/jwtex.example.com",
     "subjectFromWebIdentityToken": "repo:octo-org/octo-repo:environment:prod"
   },
   "sourceIPAddress": "104.211.45.236",
   "userAgent": "aws-cli/2.2.35 Python/3.8.8 Linux/5.8.0-1040-azure exe/x86_64.ubuntu.20 prompt/off command/sts.get-caller-identity",
   "userIdentity": {
-    "identityProvider": "arn:aws:iam::0123456789012:oidc-provider/token.actions.githubusercontent.com",
-    "principalId": "arn:aws:iam::0123456789012:oidc-provider/token.actions.githubusercontent.com:https://github.com/octo-org:repo:octo-org/octo-repo:environment:prod",
+    "identityProvider": "arn:aws:iam::0123456789012:oidc-provider/jwtex.example.com",
+    "principalId": "arn:aws:iam::0123456789012:oidc-provider/jwtex.example.com:https://github.com/octo-org:repo:octo-org/octo-repo:environment:prod",
     "type": "WebIdentityUser",
     "userName": "repo:octo-org/octo-repo:environment:prod"
   }
--
Similar Resources

A fast and simple JWT implementation for Go

A fast and simple JWT implementation for Go

JWT Fast and simple JWT implementation written in Go. This package was designed with security, performance and simplicity in mind, it protects your to

Jan 5, 2023

backend implementation demonstration in go with JWT, MongoDB and etc.

backend implementation demonstration in go with JWT, MongoDB and etc.

Nov 19, 2022

Golang implementation of JWT and Refresh Token

Fiber and JWT with Refresh Token Repo ini adalah demostrasi JWT support refresh token tanpa menggunakan storage Branch Main: unlimited refresh token R

Dec 18, 2022

Golang with JWT, Go Gin and MongoDB

User authentication in Golang with JWT, Go Gin and MongoDB Golang backend application that uses JWT tokens for users Locally Up Setup your .env file,

Sep 16, 2022

A demo of authentication and authorization using jwt

A demo of authentication and authorization using jwt

Nogopy Hi, this a demo of how to use jwt for authentication in microservices Keep in mind that this is a demo of how to authenticate using jwt, we don

Nov 1, 2021

JWT and Permission Middleware with MongoRPC

JWT and Permission Middleware with MongoRPC

Nov 19, 2021

Backend Development Rest Api Project for book management system. Used Features like redis, jwt token,validation and authorization.

Golang-restapi-project Simple Rest Api Project with Authentication, Autherization,Validation and Connection with redis File Structure ├── cache │ ├──

May 25, 2022

Generate and verify JWT tokens with Trusted Platform Module (TPM)

golang-jwt for Trusted Platform Module (TPM) This is just an extension for go-jwt i wrote over thanksgiving that allows creating and verifying JWT tok

Oct 7, 2022

Golang based User creation and Management application. GORM, Fiber, JWT

User Creation and Management app (BACK-END) Auth Features: Create Clients (regular password + 6 one-time passwords + (optional) QR code and Secret for

Dec 2, 2022
JWT wrapper library which makes it simple to use ECDSA based JWT signing

JWT JWT wrapper library which makes it simple to user ECDSA based JWT signing. Usage package main import ( "context" "github.com/infiniteloopcloud

Feb 10, 2022
Account-jwt-go - Simple JWT api with go, gorm, gin
Account-jwt-go - Simple JWT api with go, gorm, gin

Account JWT on Go Go, gorm, Gin web framework 를 활용하여 만든 간단한 JWT API 입니다. Dajngo의

Apr 14, 2022
Krakend-jwt-header-rewriter - Kraken Plugin - JWT Header Rewriter

Kraken Plugin - JWT Header Rewriter 1 Plugin Configuration Name Desciption Defau

Feb 15, 2022
an SSO and OAuth / OIDC login solution for Nginx using the auth_request module
an SSO and OAuth / OIDC login solution for Nginx using the auth_request module

Vouch Proxy An SSO solution for Nginx using the auth_request module. Vouch Proxy can protect all of your websites at once. Vouch Proxy supports many O

Jan 4, 2023
A collection of authentication Go packages related to OIDC, JWKs and Distributed Claims.

cap (collection of authentication packages) provides a collection of related packages which enable support for OIDC, JWT Verification and Distributed Claims.

Dec 7, 2022
Demonstration of sharing secret data between an OAuth/OIDC client and an Identity Providers web client.

OAuth / OIDC Cubbyhole Share secret data between client applications. This is mostly a demonstration of some of the work I've been evaluating at Storj

Mar 21, 2022
Casdoor is a UI-first centralized authentication / Single-Sign-On (SSO) platform based on OAuth 2.0 / OIDC.

A UI-first centralized authentication / Single-Sign-On (SSO) platform based on OAuth 2.0 / OIDC

Dec 29, 2022
Minting OIDC tokens from GitHub Actions for use with OpenFaaS

minty Experiment for minting OIDC tokens from GitHub Actions for use with OpenFaaS Why would you want this? Enable third-parties to deploy to your ope

Oct 31, 2021
Small library to make it easier to get a OIDC configuration

OIDC Discovery client This package covers two needs: Get the discovery document from some authority Get certificates from that authority Usage package

Nov 28, 2021
simple-jwt-provider - Simple and lightweight provider which exhibits JWTs, supports login, password-reset (via mail) and user management.

Simple and lightweight JWT-Provider written in go (golang). It exhibits JWT for the in postgres persisted user, which can be managed via api. Also, a password-reset flow via mail verification is available. User specific custom-claims also available for jwt-generation and mail rendering.

Dec 18, 2022