package apptoken import ( "context" "encoding/json" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/logical" ) const ( jwksPath = ".well-known/jwks.json" openidConfigPath = ".well-known/openid-configuration" ) func pathJWKS(b *appTokenBackend) *framework.Path { return &framework.Path{ Pattern: jwksPath + "$", DisplayAttrs: &framework.DisplayAttributes{ OperationPrefix: "apptoken", OperationSuffix: "jwks", }, Operations: map[logical.Operation]framework.OperationHandler{ logical.ReadOperation: &framework.PathOperation{Callback: b.pathJWKSRead}, }, HelpSynopsis: "Public JSON Web Key Set for offline token validation.", HelpDescription: "Unauthenticated. Returns the public signing keys (current plus retained) so services can verify app tokens without contacting Vault per request.", } } func pathOpenIDConfig(b *appTokenBackend) *framework.Path { return &framework.Path{ Pattern: openidConfigPath + "$", DisplayAttrs: &framework.DisplayAttributes{ OperationPrefix: "apptoken", OperationSuffix: "openid-configuration", }, Operations: map[logical.Operation]framework.OperationHandler{ logical.ReadOperation: &framework.PathOperation{Callback: b.pathOpenIDConfigRead}, }, HelpSynopsis: "OIDC issuer metadata.", HelpDescription: "Unauthenticated. Advertises the issuer and jwks_uri so standard OIDC/JWT clients can discover the validation keys.", } } // rawJSONResponse renders a body as an unauthenticated raw JSON HTTP response, // the form OIDC/JWKS clients expect (rather than Vault's data-wrapped JSON). func rawJSONResponse(body interface{}) (*logical.Response, error) { data, err := json.Marshal(body) if err != nil { return nil, err } return &logical.Response{ Data: map[string]interface{}{ logical.HTTPContentType: "application/json", logical.HTTPRawBody: data, logical.HTTPStatusCode: 200, }, }, nil } func (b *appTokenBackend) pathJWKSRead(ctx context.Context, req *logical.Request, _ *framework.FieldData) (*logical.Response, error) { ks, err := b.getKeyset(ctx, req.Storage) if err != nil { return nil, err } jwks, err := ks.jwks() if err != nil { return nil, err } return rawJSONResponse(jwks) } func (b *appTokenBackend) pathOpenIDConfigRead(ctx context.Context, req *logical.Request, _ *framework.FieldData) (*logical.Response, error) { cfg, err := b.getConfig(ctx, req.Storage) if err != nil { return nil, err } issuer := "" if cfg != nil { issuer = cfg.Issuer } meta := map[string]interface{}{ "issuer": issuer, "jwks_uri": issuer + "/" + jwksPath, "response_types_supported": []string{"token"}, "subject_types_supported": []string{"public"}, "id_token_signing_alg_values_supported": []string{algEdDSA, algRS256}, } return rawJSONResponse(meta) }