Self-hosted service
openpubkey/openpubkey avatar
openpubkey/openpubkey

OpenPubkey lets a Google login vouch for a public key without adding a CA

Reference implementation of OpenPubkey

911 stars74 forksGoApache-2.0

At a glance

What is it?
OpenPubkey is a protocol and reference implementation that binds an OpenID Connect identity to a public key by abusing the ID Token nonce field, producing a PK Token that verifies like a certificate without any certificate authority. It runs on unmodified providers, and the current client covers Google's provider for users and GitHub's for workloads.
Who is it for?
Adopt OpenPubkey if you want to sign artifacts under an identity your users already have, without standing up a certificate authority and without asking your identity provider to change anything, since the binding rides on the standard nonce claim.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 9 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • openpubkey/openpubkey911 ★ · +0
Star history
Repository2026-10-212026-11-07
openpubkey/openpubkey911911

0 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

What the PK Token actually proves

OpenPubkey is a protocol for leveraging OpenID Providers to bind identities to public keys. It adds user- or workload-generated public keys to OpenID Connect, enabling identities to sign messages or artifacts under their OIDC identity.

The artefact is called a PK Token. The README describes what it proves: this token proves control of the OIDC identity and the associated private key at a specific time, as long as a verifier trusts the OP.

The comparison the project draws is the one that makes the idea legible. Put another way, the PK Token provides the same assurances as a certificate issued by a Certificate Authority but critically does not require adding a CA. Instead, the OP fulfills the role of the CA.

So the usual PKI shape is inverted. Normally you run a CA, it issues certificates binding an identity to a key, and a verifier checks the certificate against the CA. Here the identity provider is already in the position of the issuer, and no new trusted party is introduced. OpenPubkey does not add any new trusted parties beyond what is required for OpenID Connect.

That last sentence is the whole security argument, and also its scope. The assurance you get is exactly the assurance OIDC already gives you about the identity, plus a binding to a key. A verifier that trusts the OP trusts the PK Token; a verifier that does not trust the OP has nothing to check against.

The token can be distributed alongside signatures in the same way as a certificate, which is the practical point for anything that signs artifacts, including container images and CI outputs.

The nonce field is the whole trick

The mechanism is a single clever move, and the README spends a numbered list on it.

In OpenID Connect, users authenticate to an OP, and the OP grants the user an ID Token. These ID Tokens are signed by the OP and contain claims made by the OP about the user, such as the user's email address. Important to OpenPubkey is the nonce claim in the ID Token.

The nonce claim is a random value sent to the OP by the user's client during authentication. OpenPubkey follows the OpenID Connect authentication protocol with the OP, but it transmits a nonce value set to the cryptographic hash of both the user's public key and a random value. So the nonce is still cryptographically random, but any party that speaks OpenPubkey can check that the ID Token contains the user's public key. From the perspective of the OP, the nonce looks just like a random value.

That is the entire hack, and it is elegant because it is invisible to the OP. Google or GitHub does not need to know that the nonce means anything special. It signs the ID Token, the ID Token contains the nonce, and the nonce commits to the public key.

The example in the README makes the arithmetic concrete. Alice's client generates a fresh key pair and a random value rz, then computes the nonce as crypto.SHA3_256(upk=alice-pubkey, alg=ES256, rz=crypto.Rand()). The alg value is set to the algorithm of Alice's key pair, so the hash commits to the signature algorithm too, which stops an attacker from presenting the same key with a different algorithm expectation.

The flow then runs as expected OIDC. The client initiates the OIDC authentication flow with the OP and sends the nonce. The OP asks Alice to consent and authenticate. If she does, the OP builds an ID Token containing the nonce claim, signs it under its signing key and sends it to Alice.

The ID Token is a JSON Web Signature with the usual structure, and the payload shows iss as the provider's issuer URL, aud as the client identifier, sub as the subject and email as the user's address.

Signing and verifying in about twenty lines of Go

The getting started section walks through a complete example, and the full code is in the examples/simple directory.

First you configure the provider, and Google is the one used in the example.

golang
opOptions := providers.GetDefaultGoogleOpOptions()
opOptions.GQSign = signGQ
op := providers.NewGoogleOpWithOptions(opOptions)

By default, OpenPubkey opens the login URI in the user's browser. Applications can configure a hook to observe or handle the URI before the library attempts to open it.

golang
err := providers.SetLoginURIHook(op, func(uri string) error {
	fmt.Println("Authorization Request URI:", uri)
	return nil
})

The hook receives the browser entry URI whether or not automatic browser opening is enabled, and setting opOptions.OpenBrowser to false before creating the provider lets the application present or open the URI itself. The same paragraph notes that the library discards its normal user-facing messages and non-fatal errors by default, and that applications can enable them with providers.SetOutWriter and providers.SetErrWriter.

Then you create the client and call Auth.

golang
opkClient, err := client.New(op)
pkt, err := opkClient.Auth(context.Background())

Auth opens a browser window to the OP, Google in this case, which prompts the user to authenticate. If the user authenticates successfully the client generates and returns the PK Token, pkt.

With the token and the client's signing key you can sign a message.

golang
msg := []byte("All is discovered - flee at once")
signedMsg, err := pkt.NewSignedMessage(msg, opkClient.GetSigner())

Verification is two steps. First check that the PK Token was issued by the OP, then use it to check the signature.

golang
pktVerifier, err := verifier.New(provider)
err = pktVerifier.VerifyPKToken(context.Background(), pkt)
msg, err := pkt.VerifySignedMessage(signedMsg)

To run the example, type go run ./examples/simple. The README states that a successful authentication produces output showing the address and the signed message.

Workload identities and the GQ signing extension

OpenPubkey supports both workload identities and user identities. The user path is the one the README walks through in detail, and the workload path is where the project extends the idea.

The GQSign option in the provider setup above is the hook for that. The repository has a gq/ directory at the top level, and the provider options carry a GQSign field alongside the other settings. The mechanism is named in the README's opening overview and in the repository listing, and it is what makes the workload case work without a user in the loop.

The distinction that matters for an adopter is which provider you point at. The README is specific: the project presently provides an OpenPubkey client and verifier for creating and verifying PK Tokens from Google's OP for users and GitHub's OP for workloads.

So today there are two supported paths, and they are for different subjects. Google covers a person authenticating in a browser. GitHub covers a workload, where the identity is an action in a pipeline rather than a human consenting to a login.

The examples directory shows the range of shapes that are documented. There is examples/simple for the message signing walkthrough, examples/deviceflow for the device authorisation flow, examples/github_actions for a CI environment, examples/gitlab for another CI system, examples/mfa for multi-factor authentication, examples/userinfo for the userinfo endpoint, examples/web for a web server, and examples/x509 for the certificate-style case. There is also a cert/ directory at the repository root.

That list is a better guide to the project's actual scope than the marketing line. A CI example and a GitLab example both point at the same need, signing something from a pipeline, and the x509 example points at the idea of presenting the result in a form existing certificate tooling understands.

The protocol is published as an IACR eprint, which the repository links as the homepage. That matters for a protocol rather than a library, because it means the design is meant to be implemented by others and reviewed independently of this Go code.

Working with providers you did not change

The compatibility claim is the reason this approach is interesting, and it deserves to be stated carefully.

OpenPubkey is fully compatible with existing OpenID Providers, Google, Azure/Microsoft, Okta, OneLogin and Keycloak, without any changes to the OpenID Provider.

That list is the important detail. The README names five providers as compatible, and the distinction to hold onto is between compatibility and support. The compatibility claim covers the protocol working against those providers, because the mechanism relies only on the nonce claim behaving as OIDC specifies. The support statement is narrower: the project presently provides a client and verifier for Google's provider for users and GitHub's for workloads.

So if you are on Okta or Keycloak, the protocol should work in principle, but you are not getting a first-class provider integration from this repository, and the provider option handling in the code would need to be checked against what your provider does.

The failure mode to think about is a provider that does something unusual with the nonce. If a provider rejects a long or structured nonce, or rewrites it, or requires it to be a particular length or format, the binding breaks. The README's design keeps the nonce looking random to the OP, but the nonce is a hash of a public key, an algorithm identifier and random bytes, so its length and shape are determined by OpenPubkey rather than by the OP. A provider with unusual nonce rules is exactly where this would fail, and the README does not document a compatibility matrix for that.

The other thing worth checking on your own provider is whether the ID Token it issues carries the nonce back unchanged in the standard claim, which is what the whole scheme depends on.

Who is using it, and what the Apache 2.0 terms mean here

Two named adopters are listed, and they are the useful signal for anyone deciding whether the protocol is real.

Docker, Inc. is building a public container registry where OpenPubkey is used to sign Docker Official Images. BastionZero uses OpenPubkey to provide secure remote access to infrastructure.

Those two cases between them cover the two halves of the design. Signing Official Images is the artifact-signing case, where the question is whether a published image can be attributed to the builder that published it. BastionZero is the access case, where the question is whether a request to your infrastructure comes from a workload with a verifiable identity.

OpenPubkey is a Linux Foundation project, and it is open source and licensed under the Apache 2.0 license. The LICENSE file is at the repository root.

For an adopter, the Apache 2.0 terms on this repository are the code licence. The question of what you can do with the PK Tokens themselves is a protocol question rather than a licence question, and the protocol is the IACR eprint. If you are signing artifacts, the licensing that matters is the one on the artifacts, and OpenPubkey's job is to give you evidence of who produced them, not to change who owns them.

On maintenance, the repository is not archived and the last push was on 2026-10-19. The listed releases are v0.29.0 on 2026-09-21, v0.28.0 on 2026-08-17 and v0.27.0 on 2026-08-03. A project that is a Linux Foundation effort with two named commercial adopters and a version still in the zero series is one to watch rather than to assume is settled.

Where OpenPubkey is the wrong tool

Four cases rule it out, and the first is simply about provider coverage.

If your identity provider is not Google or GitHub, you are outside the supported path. The README says the project presently provides a client and verifier for Google's provider for users and GitHub's for workloads. The compatibility list names Azure/Microsoft, Okta, OneLogin and Keycloak as working with the protocol, but a first-class client is a different thing from a protocol that works, and you would be building the provider integration yourself.

The second case is when you need a trust anchor that is not an identity provider. OpenPubkey's entire assurance chain terminates at the OP. If you need a key bound to an identity by something other than an OIDC login, or you need a binding that does not depend on a live relationship with an identity provider, the design does not reach there.

The third is offline or long-lived verification. A PK Token proves control of the identity at a specific time, as the README says. It is not a long-lived credential, and the verifier needs to trust the OP to check it. If your use case is verifying a signature produced months or years ago against a key that has since been revoked at the provider, that is a different problem.

The fourth is a device with no browser and no interactive consent. The default flow opens a login URI in a browser, and the deviceflow example exists for that case, but the shape of the interaction is still an OIDC authentication, and any device or automation that cannot complete one is out of scope.

There is a fifth limitation that is more subtle and worth naming for a security tool. The verifier trusts the OP. If the OP is compromised, or if an attacker can get the provider to issue an ID Token with a chosen nonce, the binding is worthless. OpenPubkey does not weaken the provider's own security, but it does make the provider's security the entire basis of the artefact you produce. That is a deliberate trade, and it is a good one when you trust Google or GitHub, and a bad one when your identity provider is something you built yourself with weaker guarantees.

The alternative for artifact signing is a real signing infrastructure: Sigstore and Fulcio, which issue short-lived certificates from an identity, or a hardware-backed key with a vendor attestation. The difference in approach is that those introduce a certificate authority and a service you run, while OpenPubkey reuses the identity provider that already exists and adds no new trusted party.

Editorial conclusion

Adopt OpenPubkey if you want to sign artifacts under an identity your users already have, without standing up a certificate authority and without asking your identity provider to change anything, since the binding rides on the standard nonce claim. Do not adopt it if your deployment needs a provider other than Google or GitHub, because the README says the project presently provides a client and verifier for creating and verifying PK Tokens from Google's provider for users and GitHub's for workloads. Before you build on it, read the provider list in the README and check that your provider's nonce handling matches the scheme, since a provider that rejects or rewrites the nonce breaks the binding.

Frequently asked questions

What is OpenPubkey?

It is a protocol and reference implementation that uses OpenID Providers to bind identities to public keys, producing a PK Token that proves control of the OIDC identity and the associated private key at a specific time, as long as a verifier trusts the provider.

How does OpenPubkey bind a public key to an identity without a certificate authority?

It transmits a nonce value to the provider set to the cryptographic hash of the user's public key and a random value. The provider signs the ID Token containing that nonce, so the token commits to the public key while still looking like an ordinary random nonce to the provider.

Which OpenID Providers does OpenPubkey support?

The README says it is fully compatible with existing providers including Google, Azure/Microsoft, Okta, OneLogin and Keycloak without any changes to the provider, but the project presently provides a client and verifier for Google's provider for users and GitHub's provider for workloads.

How do I run the OpenPubkey signing example?

Type go run ./examples/simple. The full example code is in the examples/simple directory, and a successful authentication produces output showing your address and the message that was signed.

Does OpenPubkey work with existing identity providers without modifying them?

Yes. The README states it is fully compatible with existing OpenID Providers without any changes to the OpenID Provider, because the binding rides on the standard nonce claim and looks like a random value from the provider's point of view.

What licence is OpenPubkey released under?

Apache 2.0. The README notes that OpenPubkey is a Linux Foundation project and is open source under the Apache 2.0 license, with the LICENSE file at the repository root.

Official sources

  1. License: Apache-2.0
  2. openpubkey/openpubkey on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/openpubkey-openpubkey.svg)](https://hysenlabs.com/projects/openpubkey-openpubkey)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/openpubkey-openpubkey.svg)](https://hysenlabs.com/projects/openpubkey-openpubkey)

Was this useful?