🐛 Fixed invalid authorize request parsing

This commit is contained in:
Maxim Lebedev 2022-02-03 02:12:09 +05:00
parent 926b6709fd
commit 236775fec7
Signed by: toby3d
GPG Key ID: 1F14E25B7C119FC5
3 changed files with 108 additions and 24 deletions

View File

@ -123,9 +123,7 @@ func (h *RequestHandler) Register(r *router.Router) {
chain := middleware.Chain{
middleware.CSRFWithConfig(middleware.CSRFConfig{
Skipper: func(ctx *http.RequestCtx) bool {
matched, _ := path.Match("/api/*", string(ctx.Path()))
return ctx.IsPost() && matched
return ctx.IsPost() && string(ctx.Path()) == "/authorize"
},
CookieMaxAge: 0,
CookieSameSite: http.CookieSameSiteStrictMode,
@ -175,7 +173,7 @@ func (h *RequestHandler) handleRender(ctx *http.RequestCtx) {
Printer: message.NewPrinter(tag),
}
req := new(AuthAuthorizeRequest)
req := NewAuthAuthorizeRequest()
if err := req.bind(ctx); err != nil {
ctx.SetStatusCode(http.StatusBadRequest)
web.WriteTemplate(ctx, &web.ErrorPage{
@ -232,7 +230,7 @@ func (h *RequestHandler) handleVerify(ctx *http.RequestCtx) {
encoder := json.NewEncoder(ctx)
req := new(AuthVerifyRequest)
req := NewAuthVerifyRequest()
if err := req.bind(ctx); err != nil {
ctx.SetStatusCode(http.StatusBadRequest)
@ -313,6 +311,19 @@ func (h *RequestHandler) handleExchange(ctx *http.RequestCtx) {
})
}
func NewAuthAuthorizeRequest() *AuthAuthorizeRequest {
return &AuthAuthorizeRequest{
ClientID: new(domain.ClientID),
CodeChallenge: "",
CodeChallengeMethod: domain.CodeChallengeMethodUndefined,
Me: new(domain.Me),
RedirectURI: new(domain.URL),
ResponseType: domain.ResponseTypeUndefined,
Scope: make(domain.Scopes, 0),
State: "",
}
}
func (r *AuthAuthorizeRequest) bind(ctx *http.RequestCtx) error {
indieAuthError := new(domain.Error)
if err := form.Unmarshal(ctx.QueryArgs(), r); err != nil {
@ -327,7 +338,6 @@ func (r *AuthAuthorizeRequest) bind(ctx *http.RequestCtx) error {
)
}
r.Scope = make(domain.Scopes, 0)
if err := r.Scope.UnmarshalForm(ctx.QueryArgs().Peek("scope")); err != nil {
if errors.As(err, indieAuthError) {
return indieAuthError
@ -340,6 +350,33 @@ func (r *AuthAuthorizeRequest) bind(ctx *http.RequestCtx) error {
)
}
if ctx.QueryArgs().Has("code_challenge") {
err := r.CodeChallengeMethod.UnmarshalForm(ctx.QueryArgs().Peek("code_challenge_method"))
if err != nil {
if errors.As(err, indieAuthError) {
return indieAuthError
}
return domain.NewError(
domain.ErrorCodeInvalidRequest,
err.Error(),
"",
)
}
}
if err := r.ResponseType.UnmarshalForm(ctx.QueryArgs().Peek("response_type")); err != nil {
if errors.As(err, indieAuthError) {
return indieAuthError
}
return domain.NewError(
domain.ErrorCodeUnsupportedResponseType,
err.Error(),
"",
)
}
if r.ResponseType == domain.ResponseTypeID {
r.ResponseType = domain.ResponseTypeCode
}
@ -347,6 +384,21 @@ func (r *AuthAuthorizeRequest) bind(ctx *http.RequestCtx) error {
return nil
}
func NewAuthVerifyRequest() *AuthVerifyRequest {
return &AuthVerifyRequest{
Authorize: "",
ClientID: new(domain.ClientID),
CodeChallenge: "",
CodeChallengeMethod: domain.CodeChallengeMethodUndefined,
Me: new(domain.Me),
Provider: "",
RedirectURI: new(domain.URL),
ResponseType: domain.ResponseTypeUndefined,
Scope: make(domain.Scopes, 0),
State: "",
}
}
func (r *AuthVerifyRequest) bind(ctx *http.RequestCtx) error {
indieAuthError := new(domain.Error)
@ -362,7 +414,6 @@ func (r *AuthVerifyRequest) bind(ctx *http.RequestCtx) error {
)
}
r.Scope = make(domain.Scopes, 0)
if err := r.Scope.UnmarshalForm(bytes.Join(ctx.PostArgs().PeekMulti("scope[]"), []byte(" "))); err != nil {
if errors.As(err, indieAuthError) {
return indieAuthError
@ -375,6 +426,33 @@ func (r *AuthVerifyRequest) bind(ctx *http.RequestCtx) error {
)
}
if ctx.PostArgs().Has("code_challenge") {
err := r.CodeChallengeMethod.UnmarshalForm(ctx.PostArgs().Peek("code_challenge_method"))
if err != nil {
if errors.As(err, indieAuthError) {
return indieAuthError
}
return domain.NewError(
domain.ErrorCodeInvalidRequest,
err.Error(),
"",
)
}
}
if err := r.ResponseType.UnmarshalForm(ctx.PostArgs().Peek("response_type")); err != nil {
if errors.As(err, indieAuthError) {
return indieAuthError
}
return domain.NewError(
domain.ErrorCodeUnsupportedResponseType,
err.Error(),
"",
)
}
// NOTE(toby3d): backwards-compatible support.
// See: https://aaronparecki.com/2020/12/03/1/indieauth-2020#response-type
if r.ResponseType == domain.ResponseTypeID {

View File

@ -28,3 +28,22 @@ type (
Exchange(ctx context.Context, opts ExchangeOptions) (*domain.Me, error)
}
)
var (
ErrMismatchClientID error = domain.NewError(
domain.ErrorCodeInvalidRequest,
"client's URL MUST match the client_id used in the authentication request",
"https://indieauth.net/source/#request",
)
ErrMismatchRedirectURI error = domain.NewError(
domain.ErrorCodeInvalidRequest,
"client's redirect URL MUST match the initial authentication request",
"https://indieauth.net/source/#request",
)
ErrMismatchPKCE error = domain.NewError(
domain.ErrorCodeInvalidRequest,
"code_verifier is not hashes to the same value as given in the code_challenge in the original "+
" authorization request",
"https://indieauth.net/source/#request",
)
)

View File

@ -51,29 +51,16 @@ func (useCase *authUseCase) Exchange(ctx context.Context, opts auth.ExchangeOpti
}
if opts.ClientID.String() != session.ClientID.String() {
return nil, domain.NewError(
domain.ErrorCodeInvalidRequest,
"client's URL MUST match the client_id used in the authentication request",
"https://indieauth.net/source/#request",
)
return nil, auth.ErrMismatchClientID
}
if opts.RedirectURI.String() != session.RedirectURI.String() {
return nil, domain.NewError(
domain.ErrorCodeInvalidRequest,
"client's redirect URL MUST match the initial authentication request",
"https://indieauth.net/source/#request",
)
return nil, auth.ErrMismatchRedirectURI
}
if session.CodeChallenge != "" &&
if session.CodeChallenge != "" && session.CodeChallengeMethod != domain.CodeChallengeMethodUndefined &&
!session.CodeChallengeMethod.Validate(session.CodeChallenge, opts.CodeVerifier) {
return nil, domain.NewError(
domain.ErrorCodeInvalidRequest,
"code_verifier is not hashes to the same value as given in the code_challenge in the original "+
"authorization request",
"https://indieauth.net/source/#request",
)
return nil, auth.ErrMismatchPKCE
}
return session.Me, nil