1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (C) 2026 Matthew Jackson
//! A worked example of the RFC 9728 host seam: a PROTECTED RESOURCE that publishes its own
//! metadata document and points at the authorization server in `conformance_server.rs`.
//!
//! # Why an authorization server crate ships a resource server example
//!
//! Read `src/resource_metadata.rs` first. This crate is an AUTHORIZATION SERVER. RFC 9728
//! section 3.1 places the protected resource metadata document under the RESOURCE's own
//! identifier, not under the AS's issuer, so `oauth-as` deliberately gives a host the TYPE and
//! serves nothing. That boundary is correct and this example does not move it: everything below
//! is the HOST's half, written out in full precisely because the library will not do it for you.
//!
//! Two things it is for:
//!
//! 1. **Documentation.** `ProtectedResourceMetadata` had no worked consumer anywhere in the tree.
//! A seam with no example is a seam a reader has to guess at, and the two details most likely
//! to be guessed wrong are both here: the document goes at
//! [`oauth_as::resource_metadata::well_known_path`] (the suffix is INSERTED between host and
//! path, not appended), and an unauthenticated request answers 401 with the RFC 9728 section
//! 5.1 `WWW-Authenticate: Bearer resource_metadata="..."` challenge that tells a client where
//! to find it.
//! 2. **An external judge.** The `authgent` MCP-OAuth scanner discovers an authorization server by
//! reading a resource's RFC 9728 document, and without one it returns a single "not an MCP
//! server" finding and skips every check it has. Standing this fixture up is what lets that
//! scanner reach, and form its own opinion about, THIS PROJECT'S RFC 8414 document.
//!
//! # What this is NOT, stated plainly because the distinction is the whole point
//!
//! This is a FIXTURE. It is a test resource with no resources in it: it validates no access
//! token, enforces no scope, and serves no protected data, because none of that is an
//! authorization server's job and none of it is what the document is for. It is compiled only as
//! an example, is never part of the library, and its `resource` identifier is a loopback address.
//!
//! Running it does NOT make `oauth-as` an MCP server, and a green from a scanner that reached us
//! through it says nothing about MCP conformance. What such a run CAN say is narrower and is the
//! only thing this project will claim from it: an independently authored third-party tool read
//! this crate's RFC 8414 metadata document and applied its own checks to it.
//!
//! # Environment
//!
//! * `OAUTH_RS_ADDR` (default `127.0.0.1:8915`): the address to bind. Deliberately a DIFFERENT
//! port from EITHER authorization server example, which bind `127.0.0.1:8914`
//! (`conformance_server.rs`) and `127.0.0.1:8916` (`production_server.rs`): RFC 9728 section
//! 3.1 puts this document under the resource's identifier, and serving it from the issuer's
//! origin would demonstrate the exact mistake the module docs warn about. If you move this
//! fixture to resolve a port clash, move it to a THIRD port rather than onto an issuer's.
//! * `OAUTH_RS_RESOURCE` (default `http://{OAUTH_RS_ADDR}`): the section 2 `resource` identifier.
//! Section 3.3 makes a client compare this against the identifier it built the request URL
//! from, so the default is derived from the bind address rather than configured separately.
//! * `OAUTH_AS_ISSUER` (default `http://127.0.0.1:8914`): the section 2 `authorization_servers`
//! entry. The same variable name `conformance_server.rs` reads, so one export configures both.
use Arc;
use Body;
use ;
use Response;
use get;
use Router;
use ;
async