@@ -57,16 +57,18 @@ const (
5757 acctPath = "/acme/acct/"
5858 // When we moved to authzv2, we used a "-v3" suffix to avoid confusion
5959 // regarding ACMEv2.
60- authzPath = "/acme/authz-v3/"
61- challengePath = "/acme/chall-v3/"
62- certPath = "/acme/cert/"
63- revokeCertPath = "/acme/revoke-cert"
64- buildIDPath = "/build"
65- rolloverPath = "/acme/key-change"
66- newNoncePath = "/acme/new-nonce"
67- newOrderPath = "/acme/new-order"
68- orderPath = "/acme/order/"
69- finalizeOrderPath = "/acme/finalize/"
60+ authzPath = "/acme/authz-v3/"
61+ authzPathWithAcct = "/acme/authz/"
62+ challengePath = "/acme/chall-v3/"
63+ challengePathWithAcct = "/acme/chall/"
64+ certPath = "/acme/cert/"
65+ revokeCertPath = "/acme/revoke-cert"
66+ buildIDPath = "/build"
67+ rolloverPath = "/acme/key-change"
68+ newNoncePath = "/acme/new-nonce"
69+ newOrderPath = "/acme/new-order"
70+ orderPath = "/acme/order/"
71+ finalizeOrderPath = "/acme/finalize/"
7072
7173 getAPIPrefix = "/get/"
7274 getOrderPath = getAPIPrefix + "order/"
@@ -432,13 +434,15 @@ func (wfe *WebFrontEndImpl) Handler(stats prometheus.Registerer, oTelHTTPOptions
432434 // TODO(@cpu): After November 1st, 2020 support for "GET" to the following
433435 // endpoints will be removed, leaving only POST-as-GET support.
434436 wfe .HandleFunc (m , orderPath , wfe .GetOrder , "GET" , "POST" )
435- wfe .HandleFunc (m , authzPath , wfe .Authorization , "GET" , "POST" )
436- wfe .HandleFunc (m , challengePath , wfe .Challenge , "GET" , "POST" )
437+ wfe .HandleFunc (m , authzPath , wfe .AuthorizationHandler , "GET" , "POST" )
438+ wfe .HandleFunc (m , authzPathWithAcct , wfe .AuthorizationHandlerWithAccount , "GET" , "POST" )
439+ wfe .HandleFunc (m , challengePath , wfe .ChallengeHandler , "GET" , "POST" )
440+ wfe .HandleFunc (m , challengePathWithAcct , wfe .ChallengeHandlerWithAccount , "GET" , "POST" )
437441 wfe .HandleFunc (m , certPath , wfe .Certificate , "GET" , "POST" )
438442 // Boulder-specific GET-able resource endpoints
439443 wfe .HandleFunc (m , getOrderPath , wfe .GetOrder , "GET" )
440- wfe .HandleFunc (m , getAuthzPath , wfe .Authorization , "GET" )
441- wfe .HandleFunc (m , getChallengePath , wfe .Challenge , "GET" )
444+ wfe .HandleFunc (m , getAuthzPath , wfe .AuthorizationHandler , "GET" )
445+ wfe .HandleFunc (m , getChallengePath , wfe .ChallengeHandler , "GET" )
442446 wfe .HandleFunc (m , getCertPath , wfe .Certificate , "GET" )
443447
444448 // Endpoint for draft-ietf-acme-ari
@@ -1088,31 +1092,55 @@ func (wfe *WebFrontEndImpl) RevokeCertificate(
10881092 response .WriteHeader (http .StatusOK )
10891093}
10901094
1091- // Challenge handles POST requests to challenge URLs.
1095+ // ChallengeHandler handles POST requests to challenge URLs of the form /acme/chall-v3/<authorizationID>/<challengeID> .
10921096// Such requests are clients' responses to the server's challenges.
1093- func (wfe * WebFrontEndImpl ) Challenge (
1097+ func (wfe * WebFrontEndImpl ) ChallengeHandler (
10941098 ctx context.Context ,
10951099 logEvent * web.RequestEvent ,
10961100 response http.ResponseWriter ,
10971101 request * http.Request ) {
1098- notFound := func () {
1102+ slug := strings .Split (request .URL .Path , "/" )
1103+ if len (slug ) != 2 {
10991104 wfe .sendError (response , logEvent , probs .NotFound ("No such challenge" ), nil )
1105+ return
11001106 }
1107+
1108+ wfe .Challenge (ctx , logEvent , challengePath , response , request , slug [0 ], slug [1 ])
1109+ }
1110+
1111+ // ChallengeHandlerWithAccount handles POST requests to challenge URLs of the form /acme/chall/{regID}/{authzID}/{challID}.
1112+ func (wfe * WebFrontEndImpl ) ChallengeHandlerWithAccount (
1113+ ctx context.Context ,
1114+ logEvent * web.RequestEvent ,
1115+ response http.ResponseWriter ,
1116+ request * http.Request ) {
11011117 slug := strings .Split (request .URL .Path , "/" )
1102- if len (slug ) != 2 {
1103- notFound ( )
1118+ if len (slug ) != 3 {
1119+ wfe . sendError ( response , logEvent , probs . NotFound ( "No such challenge" ), nil )
11041120 return
11051121 }
1106- authorizationID , err := strconv .ParseInt (slug [0 ], 10 , 64 )
1122+ // TODO(#7683): the regID is currently ignored.
1123+ wfe .Challenge (ctx , logEvent , challengePathWithAcct , response , request , slug [1 ], slug [2 ])
1124+ }
1125+
1126+ // Challenge handles POSTS to both formats of challenge URLs.
1127+ func (wfe * WebFrontEndImpl ) Challenge (
1128+ ctx context.Context ,
1129+ logEvent * web.RequestEvent ,
1130+ handlerPath string ,
1131+ response http.ResponseWriter ,
1132+ request * http.Request ,
1133+ authorizationIDStr string ,
1134+ challengeID string ) {
1135+ authorizationID , err := strconv .ParseInt (authorizationIDStr , 10 , 64 )
11071136 if err != nil {
11081137 wfe .sendError (response , logEvent , probs .Malformed ("Invalid authorization ID" ), nil )
11091138 return
11101139 }
1111- challengeID := slug [1 ]
11121140 authzPB , err := wfe .ra .GetAuthorization (ctx , & rapb.GetAuthorizationRequest {Id : authorizationID })
11131141 if err != nil {
11141142 if errors .Is (err , berrors .NotFound ) {
1115- notFound ( )
1143+ wfe . sendError ( response , logEvent , probs . NotFound ( "No such challenge" ), nil )
11161144 } else {
11171145 wfe .sendError (response , logEvent , web .ProblemDetailsForError (err , "Problem getting authorization" ), err )
11181146 }
@@ -1133,7 +1161,7 @@ func (wfe *WebFrontEndImpl) Challenge(
11331161 }
11341162 challengeIndex := authz .FindChallengeByStringID (challengeID )
11351163 if challengeIndex == - 1 {
1136- notFound ( )
1164+ wfe . sendError ( response , logEvent , probs . NotFound ( "No such challenge" ), nil )
11371165 return
11381166 }
11391167
@@ -1157,11 +1185,11 @@ func (wfe *WebFrontEndImpl) Challenge(
11571185 challenge := authz .Challenges [challengeIndex ]
11581186 switch request .Method {
11591187 case "GET" , "HEAD" :
1160- wfe .getChallenge (response , request , authz , & challenge , logEvent )
1188+ wfe .getChallenge (handlerPath , response , request , authz , & challenge , logEvent )
11611189
11621190 case "POST" :
11631191 logEvent .ChallengeType = string (challenge .Type )
1164- wfe .postChallenge (ctx , response , request , authz , challengeIndex , logEvent )
1192+ wfe .postChallenge (ctx , handlerPath , response , request , authz , challengeIndex , logEvent )
11651193 }
11661194}
11671195
@@ -1186,9 +1214,17 @@ func prepAccountForDisplay(acct *core.Registration) {
11861214// prepChallengeForDisplay takes a core.Challenge and prepares it for display to
11871215// the client by filling in its URL field and clearing several unnecessary
11881216// fields.
1189- func (wfe * WebFrontEndImpl ) prepChallengeForDisplay (request * http.Request , authz core.Authorization , challenge * core.Challenge ) {
1217+ func (wfe * WebFrontEndImpl ) prepChallengeForDisplay (
1218+ handlerPath string ,
1219+ request * http.Request ,
1220+ authz core.Authorization ,
1221+ challenge * core.Challenge ,
1222+ ) {
11901223 // Update the challenge URL to be relative to the HTTP request Host
11911224 challenge .URL = web .RelativeEndpoint (request , fmt .Sprintf ("%s%s/%s" , challengePath , authz .ID , challenge .StringID ()))
1225+ if handlerPath == challengePathWithAcct || handlerPath == authzPathWithAcct {
1226+ challenge .URL = web .RelativeEndpoint (request , fmt .Sprintf ("%s%d/%s/%s" , challengePathWithAcct , authz .RegistrationID , authz .ID , challenge .StringID ()))
1227+ }
11921228
11931229 // Internally, we store challenge error problems with just the short form
11941230 // (e.g. "CAA") of the problem type. But for external display, we need to
@@ -1211,9 +1247,9 @@ func (wfe *WebFrontEndImpl) prepChallengeForDisplay(request *http.Request, authz
12111247
12121248// prepAuthorizationForDisplay takes a core.Authorization and prepares it for
12131249// display to the client by preparing all its challenges.
1214- func (wfe * WebFrontEndImpl ) prepAuthorizationForDisplay (request * http.Request , authz * core.Authorization ) {
1250+ func (wfe * WebFrontEndImpl ) prepAuthorizationForDisplay (handlerPath string , request * http.Request , authz * core.Authorization ) {
12151251 for i := range authz .Challenges {
1216- wfe .prepChallengeForDisplay (request , * authz , & authz .Challenges [i ])
1252+ wfe .prepChallengeForDisplay (handlerPath , request , * authz , & authz .Challenges [i ])
12171253 }
12181254
12191255 // Shuffle the challenges so no one relies on their order.
@@ -1235,15 +1271,15 @@ func (wfe *WebFrontEndImpl) prepAuthorizationForDisplay(request *http.Request, a
12351271}
12361272
12371273func (wfe * WebFrontEndImpl ) getChallenge (
1274+ handlerPath string ,
12381275 response http.ResponseWriter ,
12391276 request * http.Request ,
12401277 authz core.Authorization ,
12411278 challenge * core.Challenge ,
12421279 logEvent * web.RequestEvent ) {
1280+ wfe .prepChallengeForDisplay (handlerPath , request , authz , challenge )
12431281
1244- wfe .prepChallengeForDisplay (request , authz , challenge )
1245-
1246- authzURL := urlForAuthz (authz , request )
1282+ authzURL := urlForAuthz (handlerPath , authz , request )
12471283 response .Header ().Add ("Location" , challenge .URL )
12481284 response .Header ().Add ("Link" , link (authzURL , "up" ))
12491285
@@ -1258,6 +1294,7 @@ func (wfe *WebFrontEndImpl) getChallenge(
12581294
12591295func (wfe * WebFrontEndImpl ) postChallenge (
12601296 ctx context.Context ,
1297+ handlerPath string ,
12611298 response http.ResponseWriter ,
12621299 request * http.Request ,
12631300 authz core.Authorization ,
@@ -1286,7 +1323,7 @@ func (wfe *WebFrontEndImpl) postChallenge(
12861323 // challenge details, not a POST to initiate a challenge
12871324 if string (body ) == "" {
12881325 challenge := authz .Challenges [challengeIndex ]
1289- wfe .getChallenge (response , request , authz , & challenge , logEvent )
1326+ wfe .getChallenge (handlerPath , response , request , authz , & challenge , logEvent )
12901327 return
12911328 }
12921329
@@ -1336,9 +1373,9 @@ func (wfe *WebFrontEndImpl) postChallenge(
13361373
13371374 // assumption: PerformValidation does not modify order of challenges
13381375 challenge := returnAuthz .Challenges [challengeIndex ]
1339- wfe .prepChallengeForDisplay (request , authz , & challenge )
1376+ wfe .prepChallengeForDisplay (handlerPath , request , authz , & challenge )
13401377
1341- authzURL := urlForAuthz (authz , request )
1378+ authzURL := urlForAuthz (handlerPath , authz , request )
13421379 response .Header ().Add ("Location" , challenge .URL )
13431380 response .Header ().Add ("Link" , link (authzURL , "up" ))
13441381
@@ -1524,11 +1561,39 @@ func (wfe *WebFrontEndImpl) deactivateAuthorization(
15241561 return true
15251562}
15261563
1527- func (wfe * WebFrontEndImpl ) Authorization (
1564+ // AuthorizationHandler handles requests to authorization URLs of the form /acme/authz/{authzID}.
1565+ func (wfe * WebFrontEndImpl ) AuthorizationHandler (
1566+ ctx context.Context ,
1567+ logEvent * web.RequestEvent ,
1568+ response http.ResponseWriter ,
1569+ request * http.Request ) {
1570+ wfe .Authorization (ctx , authzPath , logEvent , response , request , request .URL .Path )
1571+ }
1572+
1573+ // AuthorizationHandlerWithAccount handles requests to authorization URLs of the form /acme/authz/{regID}/{authzID}.
1574+ func (wfe * WebFrontEndImpl ) AuthorizationHandlerWithAccount (
15281575 ctx context.Context ,
15291576 logEvent * web.RequestEvent ,
15301577 response http.ResponseWriter ,
15311578 request * http.Request ) {
1579+ slug := strings .Split (request .URL .Path , "/" )
1580+ if len (slug ) != 2 {
1581+ wfe .sendError (response , logEvent , probs .NotFound ("No such authorization" ), nil )
1582+ return
1583+ }
1584+ // TODO(#7683): The regID is currently ignored.
1585+ wfe .Authorization (ctx , authzPathWithAcct , logEvent , response , request , slug [1 ])
1586+ }
1587+
1588+ // Authorization handles both `/acme/authz/{authzID}` and `/acme/authz/{regID}/{authzID}` requests,
1589+ // after the calling function has parsed out the authzID.
1590+ func (wfe * WebFrontEndImpl ) Authorization (
1591+ ctx context.Context ,
1592+ handlerPath string ,
1593+ logEvent * web.RequestEvent ,
1594+ response http.ResponseWriter ,
1595+ request * http.Request ,
1596+ authzIDStr string ) {
15321597 var requestAccount * core.Registration
15331598 var requestBody []byte
15341599 // If the request is a POST it is either:
@@ -1546,7 +1611,7 @@ func (wfe *WebFrontEndImpl) Authorization(
15461611 requestBody = body
15471612 }
15481613
1549- authzID , err := strconv .ParseInt (request . URL . Path , 10 , 64 )
1614+ authzID , err := strconv .ParseInt (authzIDStr , 10 , 64 )
15501615 if err != nil {
15511616 wfe .sendError (response , logEvent , probs .Malformed ("Invalid authorization ID" ), nil )
15521617 return
@@ -1615,7 +1680,7 @@ func (wfe *WebFrontEndImpl) Authorization(
16151680 return
16161681 }
16171682
1618- wfe .prepAuthorizationForDisplay (request , & authz )
1683+ wfe .prepAuthorizationForDisplay (handlerPath , request , & authz )
16191684
16201685 err = wfe .writeJsonResponse (response , logEvent , http .StatusOK , authz )
16211686 if err != nil {
@@ -2731,6 +2796,10 @@ func extractRequesterIP(req *http.Request) (net.IP, error) {
27312796 return net .ParseIP (host ), nil
27322797}
27332798
2734- func urlForAuthz (authz core.Authorization , request * http.Request ) string {
2799+ func urlForAuthz (handlerPath string , authz core.Authorization , request * http.Request ) string {
2800+ if handlerPath == challengePathWithAcct || handlerPath == authzPathWithAcct {
2801+ return web .RelativeEndpoint (request , fmt .Sprintf ("%s%d/%s" , authzPathWithAcct , authz .RegistrationID , authz .ID ))
2802+ }
2803+
27352804 return web .RelativeEndpoint (request , authzPath + authz .ID )
27362805}
0 commit comments