@@ -159,4 +159,127 @@ func TestRPCAuth(t *testing.T) {
159159
160160 node .StopDaemon ()
161161 })
162+
163+ t .Run ("Requests without Authorization header are rejected when auth is enabled" , func (t * testing.T ) {
164+ t .Parallel ()
165+
166+ node := makeAndStartProtectedNode (t , map [string ]* config.RPCAuthScope {
167+ "userA" : {
168+ AuthSecret : "bearer:mytoken" ,
169+ AllowedPaths : []string {"/api/v0" },
170+ },
171+ })
172+
173+ // Create client with NO auth
174+ apiClient := node .APIClient () // Uses http.DefaultClient with no auth headers
175+
176+ // Should be denied without auth header
177+ resp := apiClient .Post ("/api/v0/id" , nil )
178+ assert .Equal (t , 403 , resp .StatusCode )
179+
180+ // Should contain denial message
181+ assert .Contains (t , resp .Body , rpcDeniedMsg )
182+
183+ node .StopDaemon ()
184+ })
185+
186+ t .Run ("Version endpoint is always accessible even with limited AllowedPaths" , func (t * testing.T ) {
187+ t .Parallel ()
188+
189+ node := makeAndStartProtectedNode (t , map [string ]* config.RPCAuthScope {
190+ "userA" : {
191+ AuthSecret : "bearer:mytoken" ,
192+ AllowedPaths : []string {"/api/v0/id" }, // Only /id allowed
193+ },
194+ })
195+
196+ apiClient := node .APIClient ()
197+ apiClient .Client = & http.Client {
198+ Transport : auth .NewAuthorizedRoundTripper ("Bearer mytoken" , http .DefaultTransport ),
199+ }
200+
201+ // Can access /version even though not in AllowedPaths
202+ resp := apiClient .Post ("/api/v0/version" , nil )
203+ assert .Equal (t , 200 , resp .StatusCode )
204+
205+ node .StopDaemon ()
206+ })
207+
208+ t .Run ("User cannot access API with another user's secret" , func (t * testing.T ) {
209+ t .Parallel ()
210+
211+ node := makeAndStartProtectedNode (t , map [string ]* config.RPCAuthScope {
212+ "alice" : {
213+ AuthSecret : "bearer:alice-secret" ,
214+ AllowedPaths : []string {"/api/v0/id" },
215+ },
216+ "bob" : {
217+ AuthSecret : "bearer:bob-secret" ,
218+ AllowedPaths : []string {"/api/v0/config" },
219+ },
220+ })
221+
222+ // Alice tries to use Bob's secret
223+ apiClient := node .APIClient ()
224+ apiClient .Client = & http.Client {
225+ Transport : auth .NewAuthorizedRoundTripper ("Bearer bob-secret" , http .DefaultTransport ),
226+ }
227+
228+ // Bob's secret should work for Bob's paths
229+ resp := apiClient .Post ("/api/v0/config/show" , nil )
230+ assert .Equal (t , 200 , resp .StatusCode )
231+
232+ // But not for Alice's paths (Bob doesn't have access to /id)
233+ resp = apiClient .Post ("/api/v0/id" , nil )
234+ assert .Equal (t , 403 , resp .StatusCode )
235+
236+ node .StopDaemon ()
237+ })
238+
239+ t .Run ("Empty AllowedPaths denies all access except version" , func (t * testing.T ) {
240+ t .Parallel ()
241+
242+ node := makeAndStartProtectedNode (t , map [string ]* config.RPCAuthScope {
243+ "userA" : {
244+ AuthSecret : "bearer:mytoken" ,
245+ AllowedPaths : []string {}, // Empty!
246+ },
247+ })
248+
249+ apiClient := node .APIClient ()
250+ apiClient .Client = & http.Client {
251+ Transport : auth .NewAuthorizedRoundTripper ("Bearer mytoken" , http .DefaultTransport ),
252+ }
253+
254+ // Should deny everything
255+ resp := apiClient .Post ("/api/v0/id" , nil )
256+ assert .Equal (t , 403 , resp .StatusCode )
257+
258+ resp = apiClient .Post ("/api/v0/config/show" , nil )
259+ assert .Equal (t , 403 , resp .StatusCode )
260+
261+ // Except version
262+ resp = apiClient .Post ("/api/v0/version" , nil )
263+ assert .Equal (t , 200 , resp .StatusCode )
264+
265+ node .StopDaemon ()
266+ })
267+
268+ t .Run ("CLI commands fail without --api-auth when auth is enabled" , func (t * testing.T ) {
269+ t .Parallel ()
270+
271+ node := makeAndStartProtectedNode (t , map [string ]* config.RPCAuthScope {
272+ "userA" : {
273+ AuthSecret : "bearer:mytoken" ,
274+ AllowedPaths : []string {"/api/v0" },
275+ },
276+ })
277+
278+ // Try to run command without --api-auth flag
279+ resp := node .RunIPFS ("id" ) // No --api-auth flag
280+ require .Error (t , resp .Err )
281+ require .Contains (t , resp .Stderr .String (), rpcDeniedMsg )
282+
283+ node .StopDaemon ()
284+ })
162285}
0 commit comments