## The bug (scoped to `github-at`)
When you exchange a GitHub OAuth/PAT token for a registry JWT, the
handler granted `io.github.<org>/*` publish permission for **every**
organization you belong to. It derived that list from [`GET
/users/{username}/orgs`](https://docs.github.com/en/rest/orgs/orgs#list-organizations-for-a-user),
which:
1. returns only **public** org memberships, and
2. carries **no role** — an org Owner and a brand-new member look
identical.
So membership was silently treated as publishing authority. Anyone who
is (or whose public membership lists them as) a member of `acme` could
publish — and, because publish is a pure namespace-glob with no
per-server ownership check, **overwrite** — any server under
`io.github.acme/*`.
## The fix
Switch to [`GET
/user/memberships/orgs`](https://docs.github.com/en/rest/orgs/members#list-organization-memberships-for-the-authenticated-user),
which returns the **authenticated caller's role** per org (and includes
private memberships), and grant the org namespace only when the role is
`admin` (org Owner).
- **Personal namespaces are unchanged** — you always get
`io.github.<your-username>/*`.
- **Org namespaces now require Owner.** GitHub has no org-level
"maintainer" role; org membership is either `admin` (Owner) or `member`.
We grant only on `admin`.
- **`read:org` is the only scope needed.** A minimal token without it
still authenticates and still publishes to your personal namespace; a
genuine missing-scope `403` from the memberships call is treated as "no
admin orgs" rather than a hard failure, so we don't push anyone to
over-scope a token. A `403`/`429` that is actually a rate limit
(`X-RateLimit-Remaining: 0` / `Retry-After`) fails closed instead, so a
throttle can't silently strip a real Owner's org grant. Critically, the
token needs **no repository scopes** — the registry never reads or
writes code, so an org Owner can hand CI a near-powerless `read:org`
token.
The authorization decision stays stateless and delegated to GitHub — no
new storage, no new endpoints, no schema changes — consistent with
[design principle #2](docs/design/design-principles.md) (minimal
operational burden).
### Containing the CI token
The registry-side change makes the org Owner the only one who can mint
org-namespace authority. But if that Owner drops the resulting PAT into
a plain repo secret, **every repository writer** can reach it (via
branch/tag pushes that run repo-controlled build/test code in the
privileged job) — quietly re-widening publish access beyond Owners.
External fork contributors still cannot reach it by default (no secrets
on fork PRs, no branch push), barring misconfigurations like
`pull_request_target`-with-checkout or self-hosted runners on public
repos.
`docs/modelcontextprotocol-io/github-actions.mdx` documents how to
contain that blast radius:
- store the token as a **GitHub Actions Environment secret** (the PAT
example uses `environment:`),
- restrict that environment to the default branch / release tags and
require PR reviews on it, so only reviewed, maintainer-approved code can
reach the token,
- optionally add a **required environment reviewer** for explicit
per-publish approval,
- and avoid the two footguns (`pull_request_target` checkout,
self-hosted runners on public repos).
This shrinks *who holds* the org credential; it does not change the fact
that an authorized publish can still overwrite a different server in the
same namespace until per-server publisher ownership exists (the
follow-up noted below).
## Why this shape, given the alternatives
[Pree's
writeup](https://github.com/modelcontextprotocol/registry/issues/982#issuecomment-3982959559)
([Notion analysis
doc](https://www.notion.so/MCP-Registry-Authorization-Model-Analysis-Issue-982-31736f79f74980bc90c6e53de1c564c5))
is the clearest framing of the problem and the option space, laying out
three broad solutions:
- **Approach A — GitHub App as the authority layer.** Strong model, but
its natural unit of authority is a *repo*, so it needs a repo→server
mapping to authorize a namespace. The safe, flexible version of that
mapping is registry-managed state — which collapses into Approach B.
It's also GitHub-only.
- **Approach B — a registry-managed grants table** (OIDC bootstrap, or
device-flow admin check). The most capable and provider-agnostic option,
and probably where v1 wants to go. But it introduces exactly the
"registry manages an authorization database" layer we wanted to avoid
taking on right now: revocation, sync, audit, migration.
- **Approach C — scoped bearer tokens (npm-style).** Good ergonomics,
but again implies issuance/storage the registry would own.
The two-property test (does the registry control the decision? is it
granted through an act requiring real authority?) is the right lens.
This change satisfies it **without** new state: the registry makes the
decision (it filters on role), and the authority is real (live GitHub
Owner role, re-checked on every short-lived token exchange).
**What this deliberately gives up**, so reviewers can weigh it honestly:
- **Owners are always in the loop.** Only org Owners can publish org
servers; there's no delegated "this team can publish this server" — that
needs Approach B's state. Reduced flexibility in exchange for zero new
infrastructure.
- **Blast radius within an org is unchanged.** An Owner (or Owner-scoped
CI token) can still overwrite *any* server under the org namespace,
because there's still no per-server publisher ownership. That
hijack-an-existing-server problem is real but **orthogonal** to this fix
and belongs in its own change (record publisher identity per server;
enforce it on update) — a follow-up to take alongside the OIDC hardening
below.
- **OIDC is untouched and remains coarser.** The `github-oidc` path
still grants the whole `io.github.<owner>/*` namespace to any workflow
with `id-token: write`, i.e. effectively anyone with write access to any
repo in the org, and it excludes non-Actions CI (Jenkins/GitLab can't
mint GitHub Actions OIDC). The admin-PAT path here is the
higher-assurance option for org publishing; OIDC stays as a
lower-assurance Actions convenience to be tightened separately.
## Revocation
The registry JWT is **5-minute, no-refresh** (per #264/#273), so loss of
Owner status propagates within minutes on the next token exchange, with
no revocation list to maintain.
## Tests
`go test ./internal/api/handlers/v0/auth/...` passes, including:
- member-role org is **not** granted; only admin orgs are
- a missing-scope `403` (no `read:org`) still yields the personal
namespace and no error
- an admin org on a **later page** of memberships is still granted
(pagination correctness)
- a `5xx` from the memberships call **fails closed** (no token issued)
rather than degrading to personal-only
- a rate-limit `403` (`X-RateLimit-Remaining: 0` / `Retry-After`)
**fails closed** — not mistaken for a missing-scope `403`
- existing org/name-validation tests updated to the new endpoint + role
shape
`gofmt`/`go vet`/`golangci-lint` (CI-pinned v2.11.4) clean; CI green on
all checks.
## Live verification
Confirmed end-to-end against real `api.github.com` (not just mocks) by
exercising this branch's `ExchangeToken` and decoding the `permissions`
claim of the returned JWT.
A dedicated org, **Tadasant Test Org** (login `Tadasant-Test-Org`), is
Owned by one account and has a second account joined as a plain
**member**. Both memberships are `active`, verified via the same
endpoint the fix uses (`GET /user/memberships/orgs/{org}`):
| Token (role) | `io.github.Tadasant-Test-Org/*` granted? | Other
patterns granted |
|---|---|---|
| Owner (`admin`) | ✅ yes | personal `io.github.<owner>/*` |
| member | ❌ no | personal `io.github.<member>/*` + the orgs that
account actually Owns |
The member case is the meaningful one: that membership is **active and
returned** by `GET /user/memberships/orgs`, and is excluded purely
because its role is `member`, not `admin`. Under the previous `GET
/users/{username}/orgs` behavior (membership = authority, no role), the
same active membership **would have granted** the org namespace. The
role filter is also selective rather than blanket-deny — the member
account still receives the namespaces of the orgs where it genuinely
holds Owner.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Radoslav Dimitrov <radoslav@stacklok.com>
316 lines
13 KiB
Text
316 lines
13 KiB
Text
---
|
|
title: How to Authenticate When Publishing to the Official MCP Registry
|
|
sidebarTitle: Authentication
|
|
---
|
|
|
|
<Note>
|
|
The MCP Registry is currently in preview. Breaking changes or data resets may occur before general availability. If you encounter any issues, please report them on [GitHub](https://github.com/modelcontextprotocol/registry/issues).
|
|
</Note>
|
|
|
|
You must authenticate before publishing to the official MCP Registry. The MCP Registry supports different authentication methods. Which authentication method you choose determines the namespace of your server's name.
|
|
|
|
If you choose GitHub-based authentication, your server's name in `server.json` **MUST** be of the form `io.github.username/*` (or `io.github.orgname/*`). For example, `io.github.alice/weather-server`.
|
|
|
|
If you choose domain-based authentication, your server's name in `server.json` **MUST** be of the form `com.example.*/*`, where `com.example` is the reverse-DNS form of your domain name. For example, `io.modelcontextprotocol/everything`.
|
|
|
|
| Authentication | Name Format | Example Name |
|
|
| -------------- | ----------------------------------------------- | ------------------------------------ |
|
|
| GitHub-based | `io.github.username/*` or `io.github.orgname/*` | `io.github.alice/weather-server` |
|
|
| domain-based | `com.example.*/*` | `io.modelcontextprotocol/everything` |
|
|
|
|
## GitHub Authentication
|
|
|
|
GitHub authentication uses an OAuth flow initiated by the `mcp-publisher` CLI tool.
|
|
|
|
To perform GitHub authentication, navigate to your server project directory and run:
|
|
|
|
```bash
|
|
mcp-publisher login github
|
|
```
|
|
|
|
You should see output like:
|
|
|
|
```text Output
|
|
Logging in with github...
|
|
|
|
To authenticate, please:
|
|
1. Go to: https://github.com/login/device
|
|
2. Enter code: ABCD-1234
|
|
3. Authorize this application
|
|
Waiting for authorization...
|
|
```
|
|
|
|
Visit the link, follow the prompts, and enter the authorization code that was printed in the terminal (e.g., `ABCD-1234` in the above output). Once complete, go back to the terminal, and you should see output like:
|
|
|
|
```text Output
|
|
Successfully authenticated!
|
|
✓ Successfully logged in
|
|
```
|
|
|
|
### Personal vs. organization namespaces
|
|
|
|
GitHub authentication always grants your personal namespace, `io.github.<your-username>/*`.
|
|
|
|
To publish under an **organization** namespace (`io.github.<orgname>/*`), you must be an **Owner** of that organization. Ordinary org membership is no longer sufficient: the registry checks your membership role and only grants the org namespace to admins. This prevents anyone who merely belongs to an org from publishing — or overwriting — servers under the org's name.
|
|
|
|
If you authenticate with a Personal Access Token (for example in CI), the token must include the `read:org` scope for the registry to see your organization role. A token without `read:org` can still publish to your personal namespace, but org publishing will be silently unavailable. The token needs **no** repository scopes — the registry never reads or writes your code.
|
|
|
|
## DNS Authentication
|
|
|
|
DNS authentication is a domain-based authentication method that relies on a DNS TXT record.
|
|
|
|
<Warning>
|
|
The TXT record must be placed on the **apex** of your domain (e.g. `example.com`), **not** under a selector like `_mcp-auth.example.com` or `_mcp-registry.example.com`. MCP DNS auth follows SPF-style placement (apex), not DKIM-style (selector). If you put the record under a selector, the registry will not see it and authentication will fail with a generic signature error.
|
|
|
|
If you rotate keys, also remember to remove the previous TXT record from the apex — a stale record left behind will be tried first and cause verification to fail.
|
|
</Warning>
|
|
|
|
To perform DNS authentication using the `mcp-publisher` CLI tool, run the following commands in your server project directory to generate a TXT record based on a public/private key pair:
|
|
|
|
<Note>
|
|
The Ed25519 codepath requires **OpenSSL 3.0 or later**. macOS ships with LibreSSL by default (the system `openssl` binary), which does not implement Ed25519 in `genpkey` and fails with `Algorithm Ed25519 not found`. On macOS, install OpenSSL 3 (`brew install openssl@3`) and invoke it explicitly — for example, replace `openssl` with `/opt/homebrew/opt/openssl@3/bin/openssl` (Apple Silicon) or `/usr/local/opt/openssl@3/bin/openssl` (Intel) in the commands below. The ECDSA P-384 codepath works on LibreSSL.
|
|
</Note>
|
|
|
|
<CodeGroup>
|
|
|
|
```bash Ed25519
|
|
MY_DOMAIN="example.com"
|
|
|
|
# Generate public/private key pair using Ed25519
|
|
openssl genpkey -algorithm Ed25519 -out key.pem
|
|
|
|
# Generate TXT record
|
|
PUBLIC_KEY="$(openssl pkey -in key.pem -pubout -outform DER | tail -c 32 | base64)"
|
|
echo "${MY_DOMAIN}. IN TXT \"v=MCPv1; k=ed25519; p=${PUBLIC_KEY}\""
|
|
```
|
|
|
|
```bash ECDSA P-384
|
|
MY_DOMAIN="example.com"
|
|
|
|
# Generate public/private key pair using ECDSA P-384
|
|
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:secp384r1 -out key.pem
|
|
|
|
# Generate TXT record
|
|
PUBLIC_KEY="$(openssl ec -in key.pem -text -noout -conv_form compressed | grep -A4 "pub:" | tail -n +2 | tr -d ' :\n' | xxd -r -p | base64)"
|
|
echo "${MY_DOMAIN}. IN TXT \"v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY}\""
|
|
```
|
|
|
|
```bash Google KMS
|
|
MY_DOMAIN="example.com"
|
|
MY_PROJECT="myproject"
|
|
MY_KEYRING="mykeyring"
|
|
MY_KEY_NAME="mykey"
|
|
|
|
# Log in using gcloud CLI (https://cloud.google.com/sdk/docs/install)
|
|
gcloud auth login
|
|
|
|
# Set default project
|
|
gcloud config set project "${MY_PROJECT}"
|
|
|
|
# Create a keyring in your project
|
|
gcloud kms keyrings create "${MY_KEYRING}" --location global
|
|
|
|
# Create an Ed25519 signing key
|
|
gcloud kms keys create "${MY_KEY_NAME}" --default-algorithm=ec-sign-ed25519 --purpose=asymmetric-signing --keyring="${MY_KEYRING}" --location=global
|
|
|
|
# Enable Application Default Credentials (ADC) so the publisher tool can sign
|
|
gcloud auth application-default login
|
|
|
|
# Attempt login to show the public key
|
|
mcp-publisher login dns google-kms --domain="${MY_DOMAIN}" --resource="projects/${MY_PROJECT}/locations/global/keyRings/${MY_KEYRING}/cryptoKeys/${MY_KEY_NAME}/cryptoKeyVersions/1"
|
|
|
|
# Copy the "Expected proof record":
|
|
# ${MY_DOMAIN}. IN TXT "v=MCPv1; k=ed25519; p=${PUBLIC_KEY}"
|
|
```
|
|
|
|
```bash Azure Key Vault
|
|
MY_DOMAIN="example.com"
|
|
MY_SUBSCRIPTION="subscription name or ID"
|
|
MY_RESOURCE_GROUP="MyResourceGroup"
|
|
MY_KEY_VAULT="MyKeyVault"
|
|
MY_KEY_NAME="MyKey"
|
|
|
|
# Log in using Azure CLI (https://learn.microsoft.com/en-us/cli/azure/install-azure-cli)
|
|
az login
|
|
|
|
# Set default subscription
|
|
az account set --subscription "${MY_SUBSCRIPTION}"
|
|
|
|
# Create a resource group
|
|
az group create --location westus --resource-group "${MY_RESOURCE_GROUP}"
|
|
|
|
# Create a key vault
|
|
az keyvault create --name "${MY_KEY_VAULT}" --location westus --resource-group "${MY_RESOURCE_GROUP}"
|
|
|
|
# Create an ECDSA P-384 signing key
|
|
az keyvault key create --name "${MY_KEY_NAME}" --vault-name "${MY_KEY_VAULT}" --curve P-384
|
|
|
|
# Attempt login to show the public key
|
|
mcp-publisher login dns azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}"
|
|
|
|
# Copy the "Expected proof record":
|
|
# ${MY_DOMAIN}. IN TXT "v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY}"
|
|
```
|
|
|
|
</CodeGroup>
|
|
|
|
Then add the TXT record using your DNS provider's control panel. It may take several minutes for the TXT record to propagate. After the TXT record has propagated, log in using the `mcp-publisher login` command:
|
|
|
|
<CodeGroup>
|
|
|
|
```bash Ed25519
|
|
MY_DOMAIN="example.com"
|
|
|
|
PRIVATE_KEY="$(openssl pkey -in key.pem -noout -text | grep -A3 "priv:" | tail -n +2 | tr -d ' :\n')"
|
|
mcp-publisher login dns --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}"
|
|
```
|
|
|
|
```bash ECDSA P-384
|
|
MY_DOMAIN="example.com"
|
|
|
|
PRIVATE_KEY="$(openssl ec -in key.pem -noout -text | grep -A4 "priv:" | tail -n +2 | tr -d ' :\n')"
|
|
mcp-publisher login dns --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}"
|
|
```
|
|
|
|
```bash Google KMS
|
|
MY_DOMAIN="example.com"
|
|
MY_PROJECT="myproject"
|
|
MY_KEYRING="mykeyring"
|
|
MY_KEY_NAME="mykey"
|
|
|
|
mcp-publisher login dns google-kms --domain="${MY_DOMAIN}" --resource="projects/${MY_PROJECT}/locations/global/keyRings/${MY_KEYRING}/cryptoKeys/${MY_KEY_NAME}/cryptoKeyVersions/1"
|
|
```
|
|
|
|
```bash Azure Key Vault
|
|
MY_DOMAIN="example.com"
|
|
MY_KEY_VAULT="MyKeyVault"
|
|
MY_KEY_NAME="MyKey"
|
|
|
|
mcp-publisher login dns azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}"
|
|
```
|
|
|
|
</CodeGroup>
|
|
|
|
## HTTP Authentication
|
|
|
|
HTTP authentication is a domain-based authentication method that relies on a `/.well-known/mcp-registry-auth` file hosted on your domain. For example, `https://example.com/.well-known/mcp-registry-auth`.
|
|
|
|
To perform HTTP authentication using the `mcp-publisher` CLI tool, run the following commands in your server project directory to generate an `mcp-registry-auth` file based on a public/private key pair:
|
|
|
|
<Note>
|
|
As with DNS authentication, the Ed25519 codepath requires **OpenSSL 3.0 or later**. macOS's system LibreSSL does not support Ed25519 in `genpkey`. See the note in the [DNS Authentication](#dns-authentication) section for the macOS workaround.
|
|
</Note>
|
|
|
|
<CodeGroup>
|
|
|
|
```bash Ed25519
|
|
# Generate public/private key pair using Ed25519
|
|
openssl genpkey -algorithm Ed25519 -out key.pem
|
|
|
|
# Generate mcp-registry-auth file
|
|
PUBLIC_KEY="$(openssl pkey -in key.pem -pubout -outform DER | tail -c 32 | base64)"
|
|
echo "v=MCPv1; k=ed25519; p=${PUBLIC_KEY}" > mcp-registry-auth
|
|
```
|
|
|
|
```bash ECDSA P-384
|
|
# Generate public/private key pair using ECDSA P-384
|
|
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:secp384r1 -out key.pem
|
|
|
|
# Generate mcp-registry-auth file
|
|
PUBLIC_KEY="$(openssl ec -in key.pem -text -noout -conv_form compressed | grep -A4 "pub:" | tail -n +2 | tr -d ' :\n' | xxd -r -p | base64)"
|
|
echo "v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY}" > mcp-registry-auth
|
|
```
|
|
|
|
```bash Google KMS
|
|
MY_DOMAIN="example.com"
|
|
MY_PROJECT="myproject"
|
|
MY_KEYRING="mykeyring"
|
|
MY_KEY_NAME="mykey"
|
|
|
|
# Log in using gcloud CLI (https://cloud.google.com/sdk/docs/install)
|
|
gcloud auth login
|
|
|
|
# Set default project
|
|
gcloud config set project "${MY_PROJECT}"
|
|
|
|
# Create a keyring in your project
|
|
gcloud kms keyrings create "${MY_KEYRING}" --location global
|
|
|
|
# Create an Ed25519 signing key
|
|
gcloud kms keys create "${MY_KEY_NAME}" --default-algorithm=ec-sign-ed25519 --purpose=asymmetric-signing --keyring="${MY_KEYRING}" --location=global
|
|
|
|
# Enable Application Default Credentials (ADC) so the publisher tool can sign
|
|
gcloud auth application-default login
|
|
|
|
# Attempt login to show the public key
|
|
mcp-publisher login http google-kms --domain="${MY_DOMAIN}" --resource="projects/${MY_PROJECT}/locations/global/keyRings/${MY_KEYRING}/cryptoKeys/${MY_KEY_NAME}/cryptoKeyVersions/1"
|
|
|
|
# Copy the "Expected proof record" to `./mcp-registry-auth`:
|
|
# v=MCPv1; k=ed25519; p=${PUBLIC_KEY}
|
|
```
|
|
|
|
```bash Azure Key Vault
|
|
MY_DOMAIN="example.com"
|
|
MY_SUBSCRIPTION="subscription name or ID"
|
|
MY_RESOURCE_GROUP="MyResourceGroup"
|
|
MY_KEY_VAULT="MyKeyVault"
|
|
MY_KEY_NAME="MyKey"
|
|
|
|
# Log in using Azure CLI (https://learn.microsoft.com/en-us/cli/azure/install-azure-cli)
|
|
az login
|
|
|
|
# Set default subscription
|
|
az account set --subscription "${MY_SUBSCRIPTION}"
|
|
|
|
# Create a resource group
|
|
az group create --location westus --resource-group "${MY_RESOURCE_GROUP}"
|
|
|
|
# Create a key vault
|
|
az keyvault create --name "${MY_KEY_VAULT}" --location westus --resource-group "${MY_RESOURCE_GROUP}"
|
|
|
|
# Create an ECDSA P-384 signing key
|
|
az keyvault key create --name "${MY_KEY_NAME}" --vault-name "${MY_KEY_VAULT}" --curve P-384
|
|
|
|
# Attempt login to show the public key
|
|
mcp-publisher login http azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}"
|
|
|
|
# Copy the "Expected proof record" to `./mcp-registry-auth`:
|
|
# v=MCPv1; k=ecdsap384; p=${PUBLIC_KEY}
|
|
```
|
|
|
|
</CodeGroup>
|
|
|
|
Then host the `mcp-registry-auth` file at `/.well-known/mcp-registry-auth` on your domain. After the file is hosted, log in using the `mcp-publisher login` command:
|
|
|
|
<CodeGroup>
|
|
|
|
```bash Ed25519
|
|
MY_DOMAIN="example.com"
|
|
PRIVATE_KEY="$(openssl pkey -in key.pem -noout -text | grep -A3 "priv:" | tail -n +2 | tr -d ' :\n')"
|
|
mcp-publisher login http --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}"
|
|
```
|
|
|
|
```bash ECDSA P-384
|
|
MY_DOMAIN="example.com"
|
|
PRIVATE_KEY="$(openssl ec -in key.pem -noout -text | grep -A4 "priv:" | tail -n +2 | tr -d ' :\n')"
|
|
mcp-publisher login http --domain "${MY_DOMAIN}" --private-key "${PRIVATE_KEY}"
|
|
```
|
|
|
|
```bash Google KMS
|
|
MY_DOMAIN="example.com"
|
|
MY_PROJECT="myproject"
|
|
MY_KEYRING="mykeyring"
|
|
MY_KEY_NAME="mykey"
|
|
|
|
mcp-publisher login http google-kms --domain="${MY_DOMAIN}" --resource="projects/${MY_PROJECT}/locations/global/keyRings/${MY_KEYRING}/cryptoKeys/${MY_KEY_NAME}/cryptoKeyVersions/1"
|
|
```
|
|
|
|
```bash Azure Key Vault
|
|
MY_DOMAIN="example.com"
|
|
MY_KEY_VAULT="MyKeyVault"
|
|
MY_KEY_NAME="MyKey"
|
|
|
|
mcp-publisher login http azure-key-vault --domain="${MY_DOMAIN}" --vault "${MY_KEY_VAULT}" --key "${MY_KEY_NAME}"
|
|
```
|
|
|
|
</CodeGroup>
|