mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-28 18:06:34 +02:00
mcp: add rfc7591 types
This commit is contained in:
parent
f1a9401ddc
commit
9894531cb8
9 changed files with 2729 additions and 0 deletions
5
go.mod
5
go.mod
|
@ -3,6 +3,7 @@ module github.com/pomerium/pomerium
|
|||
go 1.23.8
|
||||
|
||||
require (
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250307204501-0409229c3780.1
|
||||
cloud.google.com/go/storage v1.51.0
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.4.2
|
||||
github.com/CAFxX/httpcompression v0.0.9
|
||||
|
@ -11,6 +12,7 @@ require (
|
|||
github.com/aws/aws-sdk-go-v2/config v1.29.12
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.79.0
|
||||
github.com/bits-and-blooms/bitset v1.22.0
|
||||
github.com/bufbuild/protovalidate-go v0.9.3
|
||||
github.com/caddyserver/certmagic v0.22.2
|
||||
github.com/cenkalti/backoff/v4 v4.3.0
|
||||
github.com/cloudflare/circl v1.6.0
|
||||
|
@ -118,6 +120,7 @@ require (
|
|||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/agnivade/levenshtein v1.2.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.65 // indirect
|
||||
|
@ -162,6 +165,7 @@ require (
|
|||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/cel-go v0.24.1 // indirect
|
||||
github.com/google/go-tpm v0.9.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
|
@ -215,6 +219,7 @@ require (
|
|||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/sryoya/protorand v0.0.0-20240429201223-e7440656b2a4 // indirect
|
||||
github.com/stoewer/go-strcase v1.3.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tchap/go-patricia/v2 v2.3.2 // indirect
|
||||
|
|
10
go.sum
10
go.sum
|
@ -1,3 +1,5 @@
|
|||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250307204501-0409229c3780.1 h1:zgJPqo17m28+Lf5BW4xv3PvU20BnrmTcGYrog22lLIU=
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250307204501-0409229c3780.1/go.mod h1:avRlCjnFzl98VPaeCtJ24RrV/wwHFzB8sWXhj26+n/U=
|
||||
cel.dev/expr v0.19.2 h1:V354PbqIXr9IQdwy4SYA4xa0HXaWq1BUPAGzugBY5V4=
|
||||
cel.dev/expr v0.19.2/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
|
@ -90,6 +92,8 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah
|
|||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
||||
|
@ -137,6 +141,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
|||
github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4=
|
||||
github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/bufbuild/protovalidate-go v0.9.3 h1:XvdtwQuppS3wjzGfpOirsqwN5ExH2+PiIuA/XZd3MTM=
|
||||
github.com/bufbuild/protovalidate-go v0.9.3/go.mod h1:2lUDP6fNd3wxznRNH3Nj64VB07+PySeslamkerwP6tE=
|
||||
github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA=
|
||||
github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q=
|
||||
github.com/caddyserver/certmagic v0.22.2 h1:qzZURXlrxwR5m25/jpvVeEyJHeJJMvAwe5zlMufOTQk=
|
||||
|
@ -309,6 +315,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
|
|||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/cel-go v0.24.1 h1:jsBCtxG8mM5wiUJDSGUqU0K7Mtr3w7Eyv00rw4DiZxI=
|
||||
github.com/google/cel-go v0.24.1/go.mod h1:Hdf9TqOaTNSFQA1ybQaRqATVoK7m/zcf7IMhGXP5zI8=
|
||||
github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q=
|
||||
github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
|
@ -615,6 +623,8 @@ github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqj
|
|||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||
github.com/sryoya/protorand v0.0.0-20240429201223-e7440656b2a4 h1:/jKH9ivHOUkahZs3zPfJfOmkXDFB6OdsHZ4W8gyDb/c=
|
||||
github.com/sryoya/protorand v0.0.0-20240429201223-e7440656b2a4/go.mod h1:9a23nlv6vzBeVlQq6JQCjljZ6sfzsB6aha1m5Ly1W2Y=
|
||||
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
|
||||
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
|
|
18
internal/rfc7591/buf.gen.yaml
Normal file
18
internal/rfc7591/buf.gen.yaml
Normal file
|
@ -0,0 +1,18 @@
|
|||
version: v2
|
||||
inputs:
|
||||
- directory: .
|
||||
plugins:
|
||||
- remote: buf.build/protocolbuffers/go:v1.36.5
|
||||
out: .
|
||||
opt:
|
||||
- paths=source_relative
|
||||
managed:
|
||||
enabled: true
|
||||
override:
|
||||
- file_option: go_package_prefix
|
||||
value: github.com/bufbuild/buf-examples/protovalidate/quickstart-go/start/gen
|
||||
# Don't modify any file option or field option for protovalidate. Without
|
||||
# this, generated Go will fail to compile.
|
||||
disable:
|
||||
- file_option: go_package
|
||||
module: buf.build/bufbuild/protovalidate
|
6
internal/rfc7591/buf.lock
Normal file
6
internal/rfc7591/buf.lock
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Generated by buf. DO NOT EDIT.
|
||||
version: v2
|
||||
deps:
|
||||
- name: buf.build/bufbuild/protovalidate
|
||||
commit: 7712fb530c574b95bc1d57c0877543c3
|
||||
digest: b5:b3e9c9428384357e3b73e4d5a4614328b0a4b1595b10163bbe9483fa16204749274c41797bd49b0d716479c855aa35c1172a94f471fa120ba8369637fd138829
|
11
internal/rfc7591/buf.yaml
Normal file
11
internal/rfc7591/buf.yaml
Normal file
|
@ -0,0 +1,11 @@
|
|||
version: v2
|
||||
modules:
|
||||
- path: .
|
||||
deps:
|
||||
- buf.build/bufbuild/protovalidate
|
||||
lint:
|
||||
use:
|
||||
- STANDARD
|
||||
breaking:
|
||||
use:
|
||||
- FILE
|
4
internal/rfc7591/generate.go
Normal file
4
internal/rfc7591/generate.go
Normal file
|
@ -0,0 +1,4 @@
|
|||
package rfc7591v1
|
||||
|
||||
//go:generate go run github.com/bufbuild/buf/cmd/buf@v1.53.0 dep update
|
||||
//go:generate go run github.com/bufbuild/buf/cmd/buf@v1.53.0 generate
|
2079
internal/rfc7591/types.pb.go
Normal file
2079
internal/rfc7591/types.pb.go
Normal file
File diff suppressed because it is too large
Load diff
547
internal/rfc7591/types.proto
Normal file
547
internal/rfc7591/types.proto
Normal file
|
@ -0,0 +1,547 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package ietf.rfc7591.v1;
|
||||
|
||||
import "buf/validate/validate.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "github.com/pomerium/pomerium/internal/rfc7591";
|
||||
|
||||
// Represents the JSON Web Key Set (JWK Set) structure defined in RFC 7517.
|
||||
// This contains a set of JWKs.
|
||||
message JsonWebKeySet {
|
||||
// REQUIRED. The value of the "keys" parameter is an array of JWK values.
|
||||
repeated JsonWebKey keys = 1 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).repeated .min_items = 1
|
||||
];
|
||||
}
|
||||
|
||||
// Represents a JSON Web Key (JWK) structure defined in RFC 7517.
|
||||
// A JWK is a JSON object that represents a cryptographic key.
|
||||
message JsonWebKey {
|
||||
// REQUIRED. The "kty" (key type) parameter identifies the cryptographic
|
||||
// algorithm family used with the key, such as "RSA" or "EC".
|
||||
string kty = 1 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string = {
|
||||
in : [ "RSA", "EC", "oct", "OKP" ],
|
||||
min_len : 1
|
||||
}
|
||||
];
|
||||
|
||||
// OPTIONAL. The "use" (public key use) parameter identifies the intended
|
||||
// use of the public key. Values are "sig" (signature) or "enc" (encryption).
|
||||
optional string use = 2
|
||||
[ (buf.validate.field).string = {in : [ "sig", "enc" ]} ];
|
||||
|
||||
// OPTIONAL. The "key_ops" (key operations) parameter identifies the
|
||||
// operation(s) for which the key is intended to be used.
|
||||
repeated string key_ops = 3
|
||||
[ (buf.validate.field).repeated .items.string.min_len = 1 ];
|
||||
|
||||
// OPTIONAL. The "alg" (algorithm) parameter identifies the algorithm
|
||||
// intended for use with the key.
|
||||
optional string alg = 4 [ (buf.validate.field).string.min_len = 1 ];
|
||||
|
||||
// OPTIONAL. The "kid" (key ID) parameter is used to match a specific key.
|
||||
optional string kid = 5 [ (buf.validate.field).string.min_len = 1 ];
|
||||
|
||||
// Parameters specific to the key type.
|
||||
oneof key_type_parameters {
|
||||
option (buf.validate.oneof).required =
|
||||
false; // Only required if kty demands it, checked by message rules
|
||||
|
||||
// RSA key specific parameters.
|
||||
RsaKeyParameters rsa_params = 6;
|
||||
// Elliptic Curve key specific parameters.
|
||||
EcKeyParameters ec_params = 7;
|
||||
// Symmetric key specific parameters.
|
||||
SymmetricKeyParameters symmetric_params = 8;
|
||||
// Octet Key Pair specific parameters (e.g., Ed25519).
|
||||
OkpKeyParameters okp_params = 9;
|
||||
}
|
||||
|
||||
// Message level validation to ensure specific parameters are present based on
|
||||
// kty.
|
||||
option (buf.validate.message).cel = {
|
||||
id : "jwk.kty_params.rsa",
|
||||
expression : "this.kty != 'RSA' || has(this.rsa_params)",
|
||||
message : "rsa_params are required for kty 'RSA'"
|
||||
};
|
||||
option (buf.validate.message).cel = {
|
||||
id : "jwk.kty_params.ec",
|
||||
expression : "this.kty != 'EC' || has(this.ec_params)",
|
||||
message : "ec_params are required for kty 'EC'"
|
||||
};
|
||||
option (buf.validate.message).cel = {
|
||||
id : "jwk.kty_params.oct",
|
||||
expression : "this.kty != 'oct' || has(this.symmetric_params)",
|
||||
message : "symmetric_params are required for kty 'oct'"
|
||||
};
|
||||
option (buf.validate.message).cel = {
|
||||
id : "jwk.kty_params.okp",
|
||||
expression : "this.kty != 'OKP' || has(this.okp_params)",
|
||||
message : "okp_params are required for kty 'OKP'"
|
||||
};
|
||||
}
|
||||
|
||||
// RSA specific key parameters (RFC 7518 Section 6.3).
|
||||
message RsaKeyParameters {
|
||||
// REQUIRED. The "n" (modulus) parameter contains the modulus value for the
|
||||
// RSA public key.
|
||||
string n = 1 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1
|
||||
];
|
||||
// REQUIRED. The "e" (exponent) parameter contains the exponent value for the
|
||||
// RSA public key.
|
||||
string e = 2 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1
|
||||
];
|
||||
}
|
||||
|
||||
// Elliptic Curve specific key parameters (RFC 7518 Section 6.2).
|
||||
message EcKeyParameters {
|
||||
// REQUIRED. The "crv" (curve) parameter identifies the cryptographic curve
|
||||
// used with the key.
|
||||
string crv = 1 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string = {in : [ "P-256", "P-384", "P-521" ]}
|
||||
];
|
||||
// REQUIRED. The "x" (x coordinate) parameter contains the x coordinate for
|
||||
// the Elliptic Curve point.
|
||||
string x = 2 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1
|
||||
];
|
||||
// REQUIRED. The "y" (y coordinate) parameter contains the y coordinate for
|
||||
// the Elliptic Curve point.
|
||||
string y = 3 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1
|
||||
];
|
||||
}
|
||||
|
||||
// Symmetric key specific parameters (RFC 7518 Section 6.4).
|
||||
message SymmetricKeyParameters {
|
||||
// REQUIRED. The "k" (key value) parameter contains the value of the symmetric
|
||||
// key.
|
||||
string k = 1 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1
|
||||
];
|
||||
}
|
||||
|
||||
// Octet Key Pair (OKP) specific parameters (RFC 8037 Section 2).
|
||||
message OkpKeyParameters {
|
||||
// REQUIRED. The "crv" (curve) parameter identifies the cryptographic curve
|
||||
// used with the key.
|
||||
string crv = 1 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string = {
|
||||
in : [ "Ed25519", "Ed448", "X25519", "X448" ]
|
||||
}
|
||||
];
|
||||
// REQUIRED. The "x" (x coordinate) parameter contains the public key.
|
||||
string x = 2 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1
|
||||
];
|
||||
}
|
||||
|
||||
// Represents the client metadata fields defined in RFC 7591 Section 2.
|
||||
// These values are used both as input to registration requests and output in
|
||||
// registration responses.
|
||||
message ClientMetadata {
|
||||
// Array of redirection URI strings. REQUIRED for clients using flows with
|
||||
// redirection.
|
||||
repeated string redirect_uris = 1 [ (buf.validate.field).repeated = {
|
||||
min_items : 1,
|
||||
items : {string : {uri : true, min_len : 1}}
|
||||
} ];
|
||||
|
||||
// OPTIONAL. String indicator of the requested authentication method for the
|
||||
// token endpoint. Default is "client_secret_basic".
|
||||
optional string token_endpoint_auth_method = 2
|
||||
[ (buf.validate.field).string = {
|
||||
in : [ "none", "client_secret_post", "client_secret_basic" ],
|
||||
} ];
|
||||
|
||||
// OPTIONAL. Array of OAuth 2.0 grant type strings that the client can use.
|
||||
// If omitted, defaults to ["authorization_code"].
|
||||
repeated string grant_types = 3
|
||||
[ (buf.validate.field).repeated .items.string = {
|
||||
in : [
|
||||
"authorization_code",
|
||||
"implicit",
|
||||
"password",
|
||||
"client_credentials",
|
||||
"refresh_token",
|
||||
"urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"urn:ietf:params:oauth:grant-type:saml2-bearer"
|
||||
],
|
||||
} ];
|
||||
|
||||
// OPTIONAL. Array of the OAuth 2.0 response type strings that the client can
|
||||
// use. If omitted, defaults to ["code"].
|
||||
repeated string response_types = 4
|
||||
[ (buf.validate.field).repeated .items.string = {
|
||||
in : [ "code", "token" ],
|
||||
} ];
|
||||
|
||||
// OPTIONAL. Human-readable string name of the client. RECOMMENDED.
|
||||
optional string client_name = 5
|
||||
[ (buf.validate.field).string = {min_len : 1, max_len : 255} ];
|
||||
|
||||
// OPTIONAL. Map for localized client names.
|
||||
map<string, string> client_name_localized = 6 [ (buf.validate.field).map = {
|
||||
keys : {
|
||||
string : {pattern : "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$"}
|
||||
}, // BCP 47 pattern
|
||||
values : {string : {min_len : 1, max_len : 255}}
|
||||
} ];
|
||||
|
||||
// OPTIONAL. URL string of a web page providing information about the client.
|
||||
// RECOMMENDED.
|
||||
optional string client_uri = 7 [ (buf.validate.field).string.uri = true ];
|
||||
|
||||
// OPTIONAL. Map for localized client URIs.
|
||||
map<string, string> client_uri_localized = 8 [ (buf.validate.field).map = {
|
||||
keys : {string : {pattern : "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$"}},
|
||||
values : {string : {uri : true}}
|
||||
} ];
|
||||
|
||||
// OPTIONAL. URL string that references a logo for the client.
|
||||
optional string logo_uri = 9 [ (buf.validate.field).string.uri = true ];
|
||||
|
||||
// OPTIONAL. Map for localized logo URIs.
|
||||
map<string, string> logo_uri_localized = 10 [ (buf.validate.field).map = {
|
||||
keys : {string : {pattern : "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$"}},
|
||||
values : {string : {uri : true}}
|
||||
} ];
|
||||
|
||||
// OPTIONAL. String containing a space-separated list of scope values.
|
||||
optional string scope = 11 [
|
||||
(buf.validate.field).string = {pattern : "^\\S+( \\S+)*$", min_len : 1}
|
||||
];
|
||||
|
||||
// OPTIONAL. Array of strings representing ways to contact people responsible
|
||||
// for this client.
|
||||
repeated string contacts = 12
|
||||
[ (buf.validate.field).repeated .items.string.email = true ];
|
||||
|
||||
// OPTIONAL. URL string pointing to terms of service.
|
||||
optional string tos_uri = 13 [ (buf.validate.field).string.uri = true ];
|
||||
|
||||
// OPTIONAL. Map for localized terms of service URIs.
|
||||
map<string, string> tos_uri_localized = 14 [ (buf.validate.field).map = {
|
||||
keys : {string : {pattern : "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$"}},
|
||||
values : {string : {uri : true}}
|
||||
} ];
|
||||
|
||||
// OPTIONAL. URL string pointing to privacy policy.
|
||||
optional string policy_uri = 15 [ (buf.validate.field).string.uri = true ];
|
||||
|
||||
// OPTIONAL. Map for localized policy URIs.
|
||||
map<string, string> policy_uri_localized = 16 [ (buf.validate.field).map = {
|
||||
keys : {string : {pattern : "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$"}},
|
||||
values : {string : {uri : true}}
|
||||
} ];
|
||||
|
||||
// OPTIONAL. URL string referencing the client's JWK Set document. Mutually
|
||||
// exclusive with `jwks`.
|
||||
optional string jwks_uri = 17 [ (buf.validate.field).string.uri = true ];
|
||||
|
||||
// OPTIONAL. Client's JWK Set document value. Mutually exclusive with
|
||||
// `jwks_uri`.
|
||||
optional JsonWebKeySet jwks = 18;
|
||||
|
||||
// OPTIONAL. Unique identifier string assigned by the client
|
||||
// developer/publisher.
|
||||
optional string software_id = 19
|
||||
[ (buf.validate.field).string = {min_len : 1, max_len : 255} ];
|
||||
|
||||
// OPTIONAL. Version identifier string for the client software.
|
||||
optional string software_version = 20
|
||||
[ (buf.validate.field).string = {min_len : 1, max_len : 255} ];
|
||||
|
||||
// Message level validation to ensure mutual exclusion of jwks and jwks_uri.
|
||||
option (buf.validate.message).cel = {
|
||||
id : "client_metadata.jwks_mutual_exclusion",
|
||||
expression : "!has(this.jwks_uri) || !has(this.jwks)",
|
||||
message : "jwks_uri and jwks are mutually exclusive"
|
||||
};
|
||||
|
||||
// Validation rule to ensure redirect_uris is present if needed (e.g., for
|
||||
// specific grant types). This might be better handled by application logic or
|
||||
// more complex CEL based on grant_types/response_types. Example (adjust logic
|
||||
// based on actual requirements): option (buf.validate.message).cel = {
|
||||
// id: "client_metadata.redirect_uris_required",
|
||||
// expression: "!(('authorization_code' in this.grant_types) || ('implicit'
|
||||
// in this.grant_types)) || size(this.redirect_uris) > 0", message:
|
||||
// "redirect_uris are required for authorization_code or implicit grant
|
||||
// types"
|
||||
// };
|
||||
}
|
||||
|
||||
// Represents the request sent to the Client Registration Endpoint (RFC 7591
|
||||
// Section 3.1).
|
||||
message ClientRegistrationRequest {
|
||||
// Fields correspond to ClientMetadata, indicating requested values.
|
||||
// REQUIRED for redirect flows.
|
||||
repeated string redirect_uris = 1 [ (buf.validate.field).repeated = {
|
||||
min_items : 1,
|
||||
items : {string : {uri : true, min_len : 1}}
|
||||
} ];
|
||||
// OPTIONAL. Default is "client_secret_basic".
|
||||
optional string token_endpoint_auth_method = 2
|
||||
[ (buf.validate.field).string = {
|
||||
in : [ "none", "client_secret_post", "client_secret_basic" ]
|
||||
} ];
|
||||
// OPTIONAL. Default is ["authorization_code"].
|
||||
repeated string grant_types = 3
|
||||
[ (buf.validate.field).repeated .items.string = {
|
||||
in : [
|
||||
"authorization_code",
|
||||
"implicit",
|
||||
"password",
|
||||
"client_credentials",
|
||||
"refresh_token",
|
||||
"urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"urn:ietf:params:oauth:grant-type:saml2-bearer"
|
||||
]
|
||||
} ];
|
||||
// OPTIONAL. Default is ["code"].
|
||||
repeated string response_types = 4 [
|
||||
(buf.validate.field).repeated .items.string = {in : [ "code", "token" ]}
|
||||
];
|
||||
// OPTIONAL. RECOMMENDED.
|
||||
optional string client_name = 5
|
||||
[ (buf.validate.field).string = {min_len : 1, max_len : 255} ];
|
||||
// OPTIONAL.
|
||||
map<string, string> client_name_localized = 6 [ (buf.validate.field).map = {
|
||||
keys : {string : {pattern : "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$"}},
|
||||
values : {string : {min_len : 1, max_len : 255}}
|
||||
} ];
|
||||
// OPTIONAL. RECOMMENDED.
|
||||
optional string client_uri = 7 [ (buf.validate.field).string.uri = true ];
|
||||
// OPTIONAL.
|
||||
map<string, string> client_uri_localized = 8 [ (buf.validate.field).map = {
|
||||
keys : {string : {pattern : "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$"}},
|
||||
values : {string : {uri : true}}
|
||||
} ];
|
||||
// OPTIONAL.
|
||||
optional string logo_uri = 9 [ (buf.validate.field).string.uri = true ];
|
||||
// OPTIONAL.
|
||||
map<string, string> logo_uri_localized = 10 [ (buf.validate.field).map = {
|
||||
keys : {string : {pattern : "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$"}},
|
||||
values : {string : {uri : true}}
|
||||
} ];
|
||||
// OPTIONAL.
|
||||
optional string scope = 11 [
|
||||
(buf.validate.field).string = {pattern : "^\\S+( \\S+)*$", min_len : 1}
|
||||
];
|
||||
// OPTIONAL.
|
||||
repeated string contacts = 12
|
||||
[ (buf.validate.field).repeated .items.string.email = true ];
|
||||
// OPTIONAL.
|
||||
optional string tos_uri = 13 [ (buf.validate.field).string.uri = true ];
|
||||
// OPTIONAL.
|
||||
map<string, string> tos_uri_localized = 14 [ (buf.validate.field).map = {
|
||||
keys : {string : {pattern : "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$"}},
|
||||
values : {string : {uri : true}}
|
||||
} ];
|
||||
// OPTIONAL.
|
||||
optional string policy_uri = 15 [ (buf.validate.field).string.uri = true ];
|
||||
// OPTIONAL.
|
||||
map<string, string> policy_uri_localized = 16 [ (buf.validate.field).map = {
|
||||
keys : {string : {pattern : "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$"}},
|
||||
values : {string : {uri : true}}
|
||||
} ];
|
||||
// OPTIONAL. Mutually exclusive with `jwks`.
|
||||
optional string jwks_uri = 17 [ (buf.validate.field).string.uri = true ];
|
||||
// OPTIONAL. Mutually exclusive with `jwks_uri`.
|
||||
optional JsonWebKeySet jwks = 18;
|
||||
// OPTIONAL.
|
||||
optional string software_id = 19
|
||||
[ (buf.validate.field).string = {min_len : 1, max_len : 255} ];
|
||||
// OPTIONAL.
|
||||
optional string software_version = 20
|
||||
[ (buf.validate.field).string = {min_len : 1, max_len : 255} ];
|
||||
|
||||
// OPTIONAL. A software statement containing client metadata values about the
|
||||
// client software as claims.
|
||||
optional string software_statement = 21 [ (buf.validate.field).string = {
|
||||
min_len : 1,
|
||||
pattern : "^[a-zA-Z0-9\\-_]+\\.[a-zA-Z0-9\\-_]+\\.[a-zA-Z0-9\\-_]*$"
|
||||
} ];
|
||||
|
||||
// Message level validation to ensure mutual exclusion of jwks and jwks_uri.
|
||||
option (buf.validate.message).cel = {
|
||||
id : "client_registration_request.jwks_mutual_exclusion",
|
||||
expression : "!has(this.jwks_uri) || !has(this.jwks)",
|
||||
message : "jwks_uri and jwks are mutually exclusive"
|
||||
};
|
||||
}
|
||||
|
||||
// Represents the successful response from the Client Registration Endpoint (RFC
|
||||
// 7591 Section 3.2.1).
|
||||
message ClientInformationResponse {
|
||||
// REQUIRED. OAuth 2.0 client identifier string issued by the authorization
|
||||
// server.
|
||||
string client_id = 1 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1
|
||||
];
|
||||
|
||||
// OPTIONAL. OAuth 2.0 client secret string. Only issued for confidential
|
||||
// clients.
|
||||
optional string client_secret = 2 [ (buf.validate.field).string.min_len = 1 ];
|
||||
|
||||
// OPTIONAL. Time at which the client identifier was issued (Unix timestamp,
|
||||
// seconds since epoch).
|
||||
optional int64 client_id_issued_at = 3 [ (buf.validate.field).int64.gt = 0 ];
|
||||
|
||||
// REQUIRED if "client_secret" is issued, OPTIONAL otherwise. Time at which
|
||||
// the client secret will expire (Unix timestamp, seconds since epoch), or 0
|
||||
// if it will not expire.
|
||||
optional int64 client_secret_expires_at = 4
|
||||
[ (buf.validate.field).int64.gte = 0 ];
|
||||
|
||||
// Contains all registered metadata about this client, reflecting server
|
||||
// state. REQUIRED if applicable to the client registration.
|
||||
repeated string redirect_uris = 5 [ (buf.validate.field).repeated = {
|
||||
min_items : 1,
|
||||
items : {string : {uri : true, min_len : 1}}
|
||||
} ];
|
||||
// OPTIONAL (reflects registered value, may have default).
|
||||
optional string token_endpoint_auth_method = 6
|
||||
[ (buf.validate.field).string = {
|
||||
in : [ "none", "client_secret_post", "client_secret_basic" ]
|
||||
} ];
|
||||
// OPTIONAL (reflects registered value, may have default).
|
||||
repeated string grant_types = 7
|
||||
[ (buf.validate.field).repeated .items.string = {
|
||||
in : [
|
||||
"authorization_code",
|
||||
"implicit",
|
||||
"password",
|
||||
"client_credentials",
|
||||
"refresh_token",
|
||||
"urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"urn:ietf:params:oauth:grant-type:saml2-bearer"
|
||||
]
|
||||
} ];
|
||||
// OPTIONAL (reflects registered value, may have default).
|
||||
repeated string response_types = 8 [
|
||||
(buf.validate.field).repeated .items.string = {in : [ "code", "token" ]}
|
||||
];
|
||||
// OPTIONAL (reflects registered value).
|
||||
optional string client_name = 9
|
||||
[ (buf.validate.field).string = {min_len : 1, max_len : 255} ];
|
||||
// OPTIONAL (reflects registered value).
|
||||
map<string, string> client_name_localized = 10 [ (buf.validate.field).map = {
|
||||
keys : {string : {pattern : "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$"}},
|
||||
values : {string : {min_len : 1, max_len : 255}}
|
||||
} ];
|
||||
// OPTIONAL (reflects registered value).
|
||||
optional string client_uri = 11 [ (buf.validate.field).string.uri = true ];
|
||||
// OPTIONAL (reflects registered value).
|
||||
map<string, string> client_uri_localized = 12 [ (buf.validate.field).map = {
|
||||
keys : {string : {pattern : "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$"}},
|
||||
values : {string : {uri : true}}
|
||||
} ];
|
||||
// OPTIONAL (reflects registered value).
|
||||
optional string logo_uri = 13 [ (buf.validate.field).string.uri = true ];
|
||||
// OPTIONAL (reflects registered value).
|
||||
map<string, string> logo_uri_localized = 14 [ (buf.validate.field).map = {
|
||||
keys : {string : {pattern : "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$"}},
|
||||
values : {string : {uri : true}}
|
||||
} ];
|
||||
// OPTIONAL (reflects registered value).
|
||||
optional string scope = 15 [
|
||||
(buf.validate.field).string = {pattern : "^\\S+( \\S+)*$", min_len : 1}
|
||||
];
|
||||
// OPTIONAL (reflects registered value).
|
||||
repeated string contacts = 16
|
||||
[ (buf.validate.field).repeated .items.string.email = true ];
|
||||
// OPTIONAL (reflects registered value).
|
||||
optional string tos_uri = 17 [ (buf.validate.field).string.uri = true ];
|
||||
// OPTIONAL (reflects registered value).
|
||||
map<string, string> tos_uri_localized = 18 [ (buf.validate.field).map = {
|
||||
keys : {string : {pattern : "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$"}},
|
||||
values : {string : {uri : true}}
|
||||
} ];
|
||||
// OPTIONAL (reflects registered value).
|
||||
optional string policy_uri = 19 [ (buf.validate.field).string.uri = true ];
|
||||
// OPTIONAL (reflects registered value).
|
||||
map<string, string> policy_uri_localized = 20 [ (buf.validate.field).map = {
|
||||
keys : {string : {pattern : "^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$"}},
|
||||
values : {string : {uri : true}}
|
||||
} ];
|
||||
// OPTIONAL (reflects registered value). Mutually exclusive with `jwks`.
|
||||
optional string jwks_uri = 21 [ (buf.validate.field).string.uri = true ];
|
||||
// OPTIONAL (reflects registered value). Mutually exclusive with `jwks_uri`.
|
||||
optional JsonWebKeySet jwks = 22;
|
||||
// OPTIONAL (reflects registered value).
|
||||
optional string software_id = 23
|
||||
[ (buf.validate.field).string = {min_len : 1, max_len : 255} ];
|
||||
// OPTIONAL (reflects registered value).
|
||||
optional string software_version = 24
|
||||
[ (buf.validate.field).string = {min_len : 1, max_len : 255} ];
|
||||
|
||||
// OPTIONAL. If a software statement was used in the request, it MUST be
|
||||
// returned unmodified.
|
||||
optional string software_statement = 25 [ (buf.validate.field).string = {
|
||||
min_len : 1,
|
||||
pattern : "^[a-zA-Z0-9\\-_]+\\.[a-zA-Z0-9\\-_]+\\.[a-zA-Z0-9\\-_]*$"
|
||||
} ];
|
||||
|
||||
// Message level validation
|
||||
option (buf.validate.message).cel = {
|
||||
id : "client_info_response.secret_expiry",
|
||||
// client_secret_expires_at MUST be present if client_secret is present and
|
||||
// non-empty
|
||||
expression : "(!has(this.client_secret) || this.client_secret == '') || "
|
||||
"has(this.client_secret_expires_at)",
|
||||
message : "client_secret_expires_at is required when client_secret is "
|
||||
"issued"
|
||||
};
|
||||
option (buf.validate.message).cel = {
|
||||
id : "client_info_response.jwks_mutual_exclusion",
|
||||
expression : "!has(this.jwks_uri) || !has(this.jwks)",
|
||||
message : "jwks_uri and jwks fields are mutually exclusive in the response"
|
||||
};
|
||||
}
|
||||
|
||||
// Standard error codes for client registration errors (RFC 7591 Section 3.2.2).
|
||||
enum ErrorCode {
|
||||
ERROR_CODE_UNSPECIFIED = 0;
|
||||
// The value of one or more redirection URIs is invalid.
|
||||
ERROR_CODE_INVALID_REDIRECT_URI = 1;
|
||||
// The value of one of the client metadata fields is invalid.
|
||||
ERROR_CODE_INVALID_CLIENT_METADATA = 2;
|
||||
// The software statement presented is invalid.
|
||||
ERROR_CODE_INVALID_SOFTWARE_STATEMENT = 3;
|
||||
// The software statement presented is not approved for use by this server.
|
||||
ERROR_CODE_UNAPPROVED_SOFTWARE_STATEMENT = 4;
|
||||
}
|
||||
|
||||
// Represents the error response from the Client Registration Endpoint (RFC 7591
|
||||
// Section 3.2.2).
|
||||
message ClientRegistrationErrorResponse {
|
||||
// REQUIRED. Single ASCII error code string from the ErrorCode enum.
|
||||
ErrorCode error = 1 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).enum = {
|
||||
defined_only : true,
|
||||
not_in : [ 0 ]
|
||||
}
|
||||
];
|
||||
|
||||
// OPTIONAL. Human-readable ASCII text description of the error.
|
||||
optional string error_description = 2
|
||||
[ (buf.validate.field).string = {min_len : 1, max_len : 1024} ];
|
||||
}
|
49
internal/rfc7591/types_test.go
Normal file
49
internal/rfc7591/types_test.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package rfc7591v1_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/bufbuild/protovalidate-go"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
|
||||
rfc7591 "github.com/pomerium/pomerium/internal/rfc7591"
|
||||
)
|
||||
|
||||
func TestValidation(t *testing.T) {
|
||||
v := &rfc7591.JsonWebKey{Kty: "Invalid"}
|
||||
require.ErrorContains(t, protovalidate.Validate(v), `kty: value must be in list ["RSA", "EC", "oct", "OKP"] [string.in]`)
|
||||
}
|
||||
|
||||
func TestJSONMarshal(t *testing.T) {
|
||||
data := `
|
||||
{
|
||||
"redirect_uris": [
|
||||
"http://localhost:8002/oauth/callback"
|
||||
],
|
||||
"token_endpoint_auth_method": "none",
|
||||
"grant_types": [
|
||||
"authorization_code",
|
||||
"refresh_token"
|
||||
],
|
||||
"response_types": [
|
||||
"code"
|
||||
],
|
||||
"client_name": "MCP Inspector",
|
||||
"client_uri": "https://github.com/modelcontextprotocol/inspector"
|
||||
}`
|
||||
v := &rfc7591.ClientMetadata{}
|
||||
require.NoError(t, protojson.Unmarshal([]byte(data), v))
|
||||
diff := cmp.Diff(&rfc7591.ClientMetadata{
|
||||
RedirectUris: []string{"http://localhost:8002/oauth/callback"},
|
||||
TokenEndpointAuthMethod: proto.String("none"),
|
||||
GrantTypes: []string{"authorization_code", "refresh_token"},
|
||||
ResponseTypes: []string{"code"},
|
||||
ClientName: proto.String("MCP Inspector"),
|
||||
ClientUri: proto.String("https://github.com/modelcontextprotocol/inspector"),
|
||||
}, v, protocmp.Transform())
|
||||
require.Empty(t, diff)
|
||||
}
|
Loading…
Add table
Reference in a new issue