/// <summary> /// Wraps controller action /// </summary> /// <param name="action">Action</param> /// <param name="request">ACME request. If presents wrapper validates JWS</param> /// <returns></returns> public AcmeResponse WrapAction(Action <AcmeResponse> action, AcmeRequest request, bool UseJwk = false) { var response = CreateResponse(); try { Logger.Info("Request {method} {path} {token}", request.Method, request.Path, request.Token); if (request.Method == "POST") { #region Check JWS IAccount account = null; // Parse JWT var token = request.Token; var header = token.GetProtected(); try { if (token == null) { throw new Exception("JSON Web Token is empty"); } } catch (Exception e) { throw new AcmeException(ErrorType.Unauthorized, "Cannot parse JSON Web Token", System.Net.HttpStatusCode.Unauthorized, e); } if (header.Url == null) { throw new MalformedException("The JWS header MUST have 'url' field"); } /// The "jwk" and "kid" fields are mutually exclusive. Servers MUST /// reject requests that contain both. if (header.Key != null && header.KeyID != null) { throw new MalformedException("The JWS header contains both mutually exclusive fields 'jwk' and 'kid'"); } if (UseJwk) { if (header.Key == null) { throw new AcmeException(ErrorType.IncorrectResponse, "JWS MUST contain 'jwk' field", System.Net.HttpStatusCode.BadRequest); } if (!token.Verify()) { throw new AcmeException(ErrorType.Unauthorized, "JWS signature is invalid", System.Net.HttpStatusCode.Unauthorized); } account = AccountService.FindByPublicKey(header.Key); // If a server receives a POST or POST-as-GET from a deactivated account, it MUST return an error response with status // code 401(Unauthorized) and type "urn:ietf:params:acme:error:unauthorized" } else { if (header.KeyID == null) { throw new AcmeException(ErrorType.IncorrectResponse, "JWS MUST contain 'kid' field", System.Net.HttpStatusCode.BadRequest); } account = AccountService.GetById(GetIdFromLink(header.KeyID)); if (!token.Verify(account.Key.GetPublicKey())) { throw new AcmeException(ErrorType.Unauthorized, "JWS signature is invalid", System.Net.HttpStatusCode.Unauthorized); } // Once an account is deactivated, the server MUST NOT accept further // requests authorized by that account's key // https://tools.ietf.org/html/rfc8555#section-7.3.6 } if (account != null) { AssertAccountStatus(account); } #endregion #region Check Nonce // The "nonce" header parameter MUST be carried in the protected header of the JWS. var nonce = request.Token.GetProtected().Nonce ?? throw new MalformedException("The 'nonce' header parameter doesn't present in the protected header of the JWS"); NonceService.Validate(nonce); #endregion } // Invoke action action.Invoke(response); } catch (AcmeException e) { response.StatusCode = (int)e.StatusCode; Error error = e; response.Content = error; Logger.Error(e); } catch (Exception e) { response.StatusCode = 500; // Internal Server Error Error error = e; response.Content = error; Logger.Error(e); } Logger.Info("Response {@response}", response); return(response); }