Documentation
JSR Token Authentication
This document describes how BloqrAI uses JSR tokens for package publishing and consumption in GitHub Actions workflows.
Overview
publish-jsr.yml's actual publish step (deno publish) now authenticates via JSR OIDC ("trusted publishing") — GitHub Actions' own OIDC identity, no token involved. See "Publishing via OIDC (current)" below. JSR_WORKFLOW_TOKEN remains documented here as the fallback mechanism (used briefly while OIDC was blocked — see "Known Issues"), and JSR_API_TOKEN is still the mechanism for consuming @bloqr/* packages, which OIDC doesn't cover.
JSR provides two types of tokens:
- Workflow Tokens (
JSR_WORKFLOW_TOKEN): Fallback for publishing packages from CI/CD pipelines when OIDC isn't usable. Scoped to specific namespaces/scopes (e.g.,@bloqr). - API Tokens (
JSR_API_TOKEN): Used for programmatic API access and package consumption (e.g., adding JSR packages as dependencies). General-purpose authentication.
Both tokens are stored as organization-level GitHub Action secrets in the BloqrAI organization and are scoped to the @bloqr JSR namespace.
Publishing via OIDC (current)
publish-jsr.yml's publish job has permissions: id-token: write and runs plain deno publish (no --token). deno publish auto-detects it's running in GitHub Actions with that permission and authenticates via GitHub's OIDC token — no secret needed, nothing to rotate. This requires @bloqr/compiler-core to be linked to BloqrAI/bloqr-core in JSR's package settings (already done).
Live-verified end-to-end on 2026-08-14: a real @bloqr/[email protected] patch release published successfully via OIDC after this org's GitHub Enterprise account's custom-OIDC-issuer setting was disabled (see "Known Issues").
Token Security & Rotation
- Tokens are stored as encrypted organization secrets in GitHub
- They are only accessible to workflows within the BloqrAI organization
- Tokens must be rotated regularly (recommend quarterly or when staff changes)
- Never hardcode tokens; always use
${{ secrets.JSR_WORKFLOW_TOKEN }}or${{ secrets.JSR_API_TOKEN }}
Publishing with Workflow Tokens (fallback)
publish-jsr.yml does not use this today (see "Publishing via OIDC" above) — kept here in case OIDC breaks again (e.g. the Enterprise custom-issuer setting getting re-enabled) and a quick fallback is needed.
Configuration
The JSR_WORKFLOW_TOKEN is configured in:
- Location: BloqrAI GitHub organization secrets (Settings > Secrets and variables > Actions)
- Scope:
@bloqrnamespace (access to all packages under@bloqr/*) - Permissions: Publish packages
Usage in Workflows
Pass the token via the --token flag to deno publish:
- name: Publish to JSR
run: deno publish --token ${{ secrets.JSR_WORKFLOW_TOKEN }}
Important: Use the --token flag (not environment variable) — this is how deno publish accepts the token.
Reverting to this fallback
If OIDC starts failing again, revert publish-jsr.yml's Publish to JSR step to the --token form above and remove id-token: write from the job's permissions. The rest of the workflow (checkout, Deno setup, type check, tests, lint, symbol docs, dry-run) is unaffected either way.
- Checks out the repository
- Sets up Deno v2.x
- Runs type checking, tests, and linting
- Performs a dry-run validation with
deno publish --dry-run - Publishes to JSR (OIDC today;
JSR_WORKFLOW_TOKENif reverted) - Reports any failures with actionable error messages
Consuming Packages with API Tokens
Configuration
The JSR_API_TOKEN is configured in:
- Location: BloqrAI GitHub organization secrets (Settings > Secrets and variables > Actions)
- Scope:
@bloqrnamespace (read access to all packages under@bloqr/*) - Permissions: Read/consume packages
Usage in Workflows
Set the token as an environment variable before running Deno commands that fetch JSR packages:
- name: Add JSR package dependency
env:
JSR_TOKEN: ${{ secrets.JSR_API_TOKEN }}
run: deno add jsr:@bloqr/compiler-core
Usage in Local Development
To use JSR_API_TOKEN locally, store it in a .env file (never commit to git):
export JSR_TOKEN="jsr_pat_..."
deno cache jsr:@bloqr/compiler-core
Standard Pattern Across Repositories
All BloqrAI repositories that interact with JSR should follow this pattern:
- Publishing workflows: Use
JSR_WORKFLOW_TOKENin publish steps - Consuming workflows: Use
JSR_API_TOKENwhen adding/updating JSR dependencies - Documentation: Link back to this central documentation
- Token rotation: Rotate tokens quarterly or when staff changes
Troubleshooting
error: Permission denied: insufficient permissions to publish
- Check that
JSR_WORKFLOW_TOKENis configured as an organization secret - Verify the token's scope includes the
@bloqrnamespace - Confirm the package is linked to the GitHub repository in JSR settings
error: could not find JSR token
- Ensure the environment variable is named
JSR_TOKEN(notJSR_WORKFLOW_TOKEN) - Verify
${{ secrets.JSR_WORKFLOW_TOKEN }}is set correctly in the workflow
error: failed to resolve jsr:@bloqr/...
- When consuming packages, ensure
JSR_API_TOKENis configured for read access - Verify the package is published and accessible in JSR
- Check that
deno.lockis not stale (regenerate if needed)
Related Documentation
- JSR Publishing Documentation
- JSR API Authentication
- Deno Publishing Guide
- @bloqr/compiler-core JSR Package
Known Issues
OIDC Publishing — resolved, was never a JSR bug (#291)
OIDC-based trusted publishing from GitHub Actions previously failed with InvalidIssuer (tracked in bloqr-core#291 and reported upstream as jsr-io/jsr#1485). At the time, org settings showed no OIDC policy configured — because the actual cause lives at the GitHub Enterprise level, not the org level, so it wasn't visible from where the investigation looked.
Root cause: this org's GitHub Enterprise account ("Bloqr Systems") had "Use enterprise-specific issuer URL" enabled under Enterprise Settings → Policies → Actions → OIDC Configuration. That customizes every GitHub Actions OIDC token's iss claim to https://token.actions.githubusercontent.com/bloqrsystems instead of the plain https://token.actions.githubusercontent.com. JSR's OIDC verifier expects the plain issuer and rejects the customized one as invalid — hence InvalidIssuer.
This was confirmed, not just inferred: crates.io's Trusted Publishing hit the identical failure mode against the same repo at the same time, but with a more descriptive rejection message (Unsupported JWT issuer: https://token.actions.githubusercontent.com/bloqrsystems) that named the actual issuer string. That setting was disabled org-wide, and both crates.io and JSR OIDC were then live-verified end-to-end with real version publishes — no JSR-side or Deno-side fix was needed.
If InvalidIssuer reappears: check Enterprise Settings → Policies → Actions → OIDC Configuration → "Use enterprise-specific issuer URL" before assuming JSR or this workflow regressed.
Token Rotation Checklist
When rotating tokens:
- Create new
JSR_WORKFLOW_TOKENon JSR - Create new
JSR_API_TOKENon JSR - Update both secrets in BloqrAI GitHub organization
- Test publishing workflow with new token
- Test dependency consumption with new token
- Delete old tokens from JSR
- Update this document with rotation date (optional)
Last Updated: 2026-08-14
Owned By: @BloqrAI/core-team