Add the service-to-service module
This commit is contained in:
26
service-to-service/docs/output/01-user-token.txt
Normal file
26
service-to-service/docs/output/01-user-token.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
==============================================================================
|
||||
docs/output/01-user-token.txt
|
||||
A complete authorization_code + PKCE flow, driven by curl. No browser, no OIDC library.
|
||||
scripts/user-token.sh, then scripts/claims.sh
|
||||
==============================================================================
|
||||
|
||||
{
|
||||
"alg": "RS256",
|
||||
"kid": "<uuid>"
|
||||
}
|
||||
{
|
||||
"aud": "downstream-api",
|
||||
"exp": <epoch>,
|
||||
"iat": <epoch>,
|
||||
"iss": "http://127.0.0.1:9000",
|
||||
"jti": "<uuid>",
|
||||
"nbf": <epoch>,
|
||||
"scope": [
|
||||
"orders.write",
|
||||
"orders.read"
|
||||
],
|
||||
"sub": "alice"
|
||||
}
|
||||
|
||||
# sub is the human. scope is what the human consented to. aud names the service the
|
||||
# token was minted for - chapter 4 is about whether anybody looks at it.
|
||||
110
service-to-service/docs/output/02-five-strategies.txt
Normal file
110
service-to-service/docs/output/02-five-strategies.txt
Normal file
@@ -0,0 +1,110 @@
|
||||
==============================================================================
|
||||
docs/output/02-five-strategies.txt
|
||||
The same request into the edge service, five ways of getting a token for the hop to
|
||||
downstream. Read the sub and scope of each downstream response.
|
||||
GET /edge/{naive,relay,client-credentials,exchange,relay-async}
|
||||
==============================================================================
|
||||
|
||||
$ curl -H "Authorization: Bearer $TOKEN" 127.0.0.1:8081/edge/naive
|
||||
{
|
||||
"strategy": "no token forwarded",
|
||||
"error": "Unauthorized: 401 Unauthorized: [no body]"
|
||||
}
|
||||
|
||||
$ curl -H "Authorization: Bearer $TOKEN" 127.0.0.1:8081/edge/relay
|
||||
{
|
||||
"strategy": "bearer token relayed from the incoming request",
|
||||
"downstream": {
|
||||
"service": "downstream:8082",
|
||||
"strictValidation": false,
|
||||
"sub": "alice",
|
||||
"aud": [
|
||||
"downstream-api"
|
||||
],
|
||||
"iss": "http://127.0.0.1:9000",
|
||||
"scope": "[orders.write, orders.read]",
|
||||
"client_id": null,
|
||||
"authorities": [
|
||||
"FactorGrantedAuthority [authority=FACTOR_BEARER, issuedAt=<timestamp>]",
|
||||
"SCOPE_orders.read",
|
||||
"SCOPE_orders.write"
|
||||
],
|
||||
"cnf": null,
|
||||
"orders": [
|
||||
{
|
||||
"total": "42.00",
|
||||
"id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
$ curl -H "Authorization: Bearer $TOKEN" 127.0.0.1:8081/edge/client-credentials
|
||||
{
|
||||
"strategy": "the edge service's own client_credentials token",
|
||||
"downstream": {
|
||||
"service": "downstream:8082",
|
||||
"strictValidation": false,
|
||||
"sub": "edge-service",
|
||||
"aud": [
|
||||
"downstream-api"
|
||||
],
|
||||
"iss": "http://127.0.0.1:9000",
|
||||
"scope": "[orders.read]",
|
||||
"client_id": null,
|
||||
"authorities": [
|
||||
"FactorGrantedAuthority [authority=FACTOR_BEARER, issuedAt=<timestamp>]",
|
||||
"SCOPE_orders.read"
|
||||
],
|
||||
"cnf": null,
|
||||
"orders": [
|
||||
{
|
||||
"total": "42.00",
|
||||
"id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
$ curl -H "Authorization: Bearer $TOKEN" 127.0.0.1:8081/edge/exchange
|
||||
{
|
||||
"strategy": "RFC 8693 token exchange",
|
||||
"downstream": {
|
||||
"service": "downstream:8082",
|
||||
"strictValidation": false,
|
||||
"sub": "alice",
|
||||
"aud": [
|
||||
"downstream-api"
|
||||
],
|
||||
"iss": "http://127.0.0.1:9000",
|
||||
"scope": "[orders.read]",
|
||||
"client_id": null,
|
||||
"authorities": [
|
||||
"SCOPE_orders.read",
|
||||
"FactorGrantedAuthority [authority=FACTOR_BEARER, issuedAt=<timestamp>]"
|
||||
],
|
||||
"cnf": null,
|
||||
"orders": [
|
||||
{
|
||||
"total": "42.00",
|
||||
"id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
$ curl -H "Authorization: Bearer $TOKEN" 127.0.0.1:8081/edge/relay-async
|
||||
{
|
||||
"strategy": "relay attempted from a separate thread",
|
||||
"error": "Unauthorized: 401 Unauthorized: [no body]"
|
||||
}
|
||||
|
||||
# naive - 401. The control.
|
||||
# relay - sub: alice, scope: [orders.write, orders.read]. The user's own
|
||||
# token, unchanged, including scopes downstream did not need.
|
||||
# client-creds - sub: edge-service, scope: [orders.read]. Correctly scoped, and
|
||||
# the user has disappeared from downstream's audit log.
|
||||
# exchange - sub: alice, scope: [orders.read]. Both. This is what RFC 8693 is
|
||||
# for and it is the one nobody reaches for.
|
||||
# relay-async - 401. The relay interceptor reads SecurityContextHolder, which is
|
||||
# a ThreadLocal, and the call was made on a different thread.
|
||||
53
service-to-service/docs/output/03-audience-ignored.txt
Normal file
53
service-to-service/docs/output/03-audience-ignored.txt
Normal file
@@ -0,0 +1,53 @@
|
||||
==============================================================================
|
||||
docs/output/03-audience-ignored.txt
|
||||
A token minted for a DIFFERENT service, presented to the downstream service.
|
||||
Default validators.
|
||||
==============================================================================
|
||||
|
||||
# the token reporting-service was issued:
|
||||
{
|
||||
"alg": "RS256",
|
||||
"kid": "<uuid>"
|
||||
}
|
||||
{
|
||||
"aud": "reporting-api",
|
||||
"exp": <epoch>,
|
||||
"iat": <epoch>,
|
||||
"iss": "http://127.0.0.1:9000",
|
||||
"jti": "<uuid>",
|
||||
"nbf": <epoch>,
|
||||
"scope": [
|
||||
"orders.read"
|
||||
],
|
||||
"sub": "reporting-service"
|
||||
}
|
||||
|
||||
$ curl -H 'Authorization: Bearer <reporting-service token>' 127.0.0.1:8082/orders
|
||||
{
|
||||
"service": "downstream:8082",
|
||||
"strictValidation": false,
|
||||
"sub": "reporting-service",
|
||||
"aud": [
|
||||
"reporting-api"
|
||||
],
|
||||
"iss": "http://127.0.0.1:9000",
|
||||
"scope": "[orders.read]",
|
||||
"client_id": null,
|
||||
"authorities": [
|
||||
"FactorGrantedAuthority [authority=FACTOR_BEARER, issuedAt=<timestamp>]",
|
||||
"SCOPE_orders.read"
|
||||
],
|
||||
"cnf": null,
|
||||
"orders": [
|
||||
{
|
||||
"total": "42.00",
|
||||
"id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# HTTP 200. The aud claim says reporting-api. The service is downstream-api.
|
||||
# JwtValidators.createDefault() is a DelegatingOAuth2TokenValidator over three
|
||||
# validators - JwtTypeValidator, JwtTimestampValidator and
|
||||
# X509CertificateThumbprintValidator. Structure, expiry, and certificate binding.
|
||||
# No issuer. No audience. Read back by reflection in ValidatorContractTests.
|
||||
80
service-to-service/docs/output/04-gateway-token-relay.txt
Normal file
80
service-to-service/docs/output/04-gateway-token-relay.txt
Normal file
@@ -0,0 +1,80 @@
|
||||
==============================================================================
|
||||
docs/output/04-gateway-token-relay.txt
|
||||
Spring Cloud Gateway Server MVC with 'filters: - TokenRelay='.
|
||||
GET /edge/relay through the gateway on 8080.
|
||||
==============================================================================
|
||||
|
||||
$ curl -H "Authorization: Bearer $TOKEN" 127.0.0.1:8080/edge/relay
|
||||
HTTP/1.1 200
|
||||
cache-control: no-cache, no-store, max-age=0, must-revalidate
|
||||
expires: 0
|
||||
pragma: no-cache
|
||||
x-content-type-options: nosniff
|
||||
x-frame-options: DENY
|
||||
x-xss-protection: 0
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"strategy": "bearer token relayed from the incoming request",
|
||||
"downstream": {
|
||||
"service": "downstream:8082",
|
||||
"strictValidation": false,
|
||||
"sub": "alice",
|
||||
"aud": [
|
||||
"downstream-api"
|
||||
],
|
||||
"iss": "http://127.0.0.1:9000",
|
||||
"scope": "[orders.write, orders.read]",
|
||||
"client_id": null,
|
||||
"authorities": [
|
||||
"SCOPE_orders.read",
|
||||
"FactorGrantedAuthority [authority=FACTOR_BEARER, issuedAt=<timestamp>]",
|
||||
"SCOPE_orders.write"
|
||||
],
|
||||
"cnf": null,
|
||||
"orders": [
|
||||
{
|
||||
"total": "42.00",
|
||||
"id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
$ curl 127.0.0.1:8080/edge/relay # no Authorization header at all
|
||||
status 401
|
||||
|
||||
# and the identical route with the TokenRelay filter REMOVED:
|
||||
$ curl -H "Authorization: Bearer $TOKEN" 127.0.0.1:8080/norelay/x
|
||||
{
|
||||
"strategy": "bearer token relayed from the incoming request",
|
||||
"downstream": {
|
||||
"service": "downstream:8082",
|
||||
"strictValidation": false,
|
||||
"sub": "alice",
|
||||
"aud": [
|
||||
"downstream-api"
|
||||
],
|
||||
"iss": "http://127.0.0.1:9000",
|
||||
"scope": "[orders.write, orders.read]",
|
||||
"client_id": null,
|
||||
"authorities": [
|
||||
"SCOPE_orders.read",
|
||||
"FactorGrantedAuthority [authority=FACTOR_BEARER, issuedAt=<timestamp>]",
|
||||
"SCOPE_orders.write"
|
||||
],
|
||||
"cnf": null,
|
||||
"orders": [
|
||||
{
|
||||
"total": "42.00",
|
||||
"id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# TokenRelay relays the access token of the currently authenticated USER - the one
|
||||
# obtained by oauth2Login(). This gateway has no oauth2Login, so there is no
|
||||
# authorized client to read a token from, and the filter contributes nothing. What
|
||||
# reaches the edge service is whatever Authorization header the caller sent, because
|
||||
# the gateway proxied it. TokenRelay is not 'forward the incoming bearer token'.
|
||||
43
service-to-service/docs/output/05-strict-validation.txt
Normal file
43
service-to-service/docs/output/05-strict-validation.txt
Normal file
@@ -0,0 +1,43 @@
|
||||
==============================================================================
|
||||
docs/output/05-strict-validation.txt
|
||||
The same tokens against JwtValidators.createAtJwtValidator().issuer(..).audience(..),
|
||||
with the authorization server emitting RFC 9068 tokens (typ: at+jwt, client_id claim).
|
||||
STRICT=true ./scripts/run.sh
|
||||
==============================================================================
|
||||
|
||||
# the wrong-audience token that was accepted in 03:
|
||||
HTTP/1.1 401
|
||||
WWW-Authenticate: Bearer error="invalid_token", error_description="An error occurred while attempting to decode the Jwt: The aud claim is not valid", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1", resource_metadata="http://127.0.0.1:8082/.well-known/oauth-protected-resource"
|
||||
|
||||
# a token minted for this service:
|
||||
{
|
||||
"service": "downstream:8082",
|
||||
"strictValidation": true,
|
||||
"sub": "edge-service",
|
||||
"aud": [
|
||||
"downstream-api"
|
||||
],
|
||||
"iss": "http://127.0.0.1:9000",
|
||||
"scope": "[orders.read]",
|
||||
"client_id": "edge-service",
|
||||
"authorities": [
|
||||
"FactorGrantedAuthority [authority=FACTOR_BEARER, issuedAt=<timestamp>]",
|
||||
"SCOPE_orders.read"
|
||||
],
|
||||
"cnf": null,
|
||||
"orders": [
|
||||
{
|
||||
"id": 1,
|
||||
"total": "42.00"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# and the edge service, which was NOT updated - it still uses Boot's
|
||||
# auto-configured decoder:
|
||||
HTTP/1.1 401
|
||||
WWW-Authenticate: Bearer error="invalid_token", error_description="An error occurred while attempting to decode the Jwt: the given typ value needs to be one of [JWT]", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1", resource_metadata="http://127.0.0.1:8081/.well-known/oauth-protected-resource"
|
||||
|
||||
# Turning on RFC 9068 at the authorization server broke every resource server that
|
||||
# still has NimbusJwtDecoder's default JOSE type verifier, and the error message
|
||||
# mentions neither RFC 9068 nor the authorization server.
|
||||
43
service-to-service/docs/output/06-mtls.txt
Normal file
43
service-to-service/docs/output/06-mtls.txt
Normal file
@@ -0,0 +1,43 @@
|
||||
==============================================================================
|
||||
docs/output/06-mtls.txt
|
||||
Client-certificate authentication on port 8443, server.ssl.client-auth=need.
|
||||
Certificates from scripts/certs.sh. edge.crt and rogue.crt have IDENTICAL subjects and
|
||||
different issuers.
|
||||
==============================================================================
|
||||
|
||||
$ openssl x509 -in target/certs/edge.crt -noout -subject -issuer
|
||||
subject=CN = edge-service, OU = payments
|
||||
issuer=CN = Internal Mesh CA
|
||||
$ openssl x509 -in target/certs/rogue.crt -noout -subject -issuer
|
||||
subject=CN = edge-service, OU = payments
|
||||
issuer=CN = Some Other CA
|
||||
|
||||
$ curl --cert edge.crt --key edge.key https://localhost:8443/mtls/whoami
|
||||
{
|
||||
"principal": "edge-service",
|
||||
"authenticationType": "PreAuthenticatedAuthenticationToken",
|
||||
"authorities": [
|
||||
"ROLE_SERVICE",
|
||||
"FACTOR_X509"
|
||||
],
|
||||
"certificateSubject": "OU=payments,CN=edge-service",
|
||||
"certificateIssuer": "CN=Internal Mesh CA"
|
||||
}
|
||||
|
||||
$ curl --cert rogue.crt --key rogue.key https://localhost:8443/mtls/whoami
|
||||
[curl exit 56, http 000]
|
||||
|
||||
$ curl https://localhost:8443/mtls/trusted-header # a permitAll() endpoint
|
||||
[curl exit 56, http 000]
|
||||
|
||||
$ curl --cert edge.crt --key edge.key -H 'X-Client-Cert-Subject: CN=payments-service' \
|
||||
https://localhost:8443/mtls/trusted-header
|
||||
{"caller":"CN=payments-service","verifiedBy":"nothing. This endpoint believes a header."}
|
||||
|
||||
# Three things worth reading twice.
|
||||
# 1. The rogue certificate fails with curl exit 56 and NO http status. The handshake
|
||||
# is rejected; the application never sees a request and logs nothing at INFO.
|
||||
# 2. So does the permitAll() endpoint. client-auth=need is a property of the
|
||||
# CONNECTOR, not of a path. You cannot expose a public endpoint on that port.
|
||||
# 3. The last call is what a mesh deployment usually looks like from inside the
|
||||
# application: an identity taken from a header, verified by nothing.
|
||||
8
service-to-service/docs/output/07-tests.txt
Normal file
8
service-to-service/docs/output/07-tests.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
==============================================================================
|
||||
docs/output/07-tests.txt
|
||||
mvn -B test
|
||||
==============================================================================
|
||||
|
||||
[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.225 s -- in com.ankurm.s2s.ValidatorContractTests
|
||||
[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
|
||||
[INFO] BUILD SUCCESS
|
||||
Reference in New Issue
Block a user