-
Notifications
You must be signed in to change notification settings - Fork 87
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
initial work on security schema (only jwt bearer) #143
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding the feature!
Certainly supporting only JWT to begin with is a very good start.
@ota42y You can clone my branch. It is almost complete but problem with the Update: This is done! 🎉 |
@ota42y Mostly done. I will figure out how to get the headers to check for a JWT token, or if it is a valid token. I've done some others, i'm very appreciative of your time you took to answer me, 🙇♂️, so i contributed some PRs for other stuff that is missing (servers is done, info is not that important but it's simple so i decided to do it) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've finished it including tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!!! I think it is a very good code.
Some of the validation was not up to OpenAPI Specification, so that needs to be fixed, but I think we're almost there!
def validate_security_schemes(securityScheme, headers) | ||
if self.type == "http" && self.scheme == "bearer" && self.bearer_format == "JWT" | ||
raise "need authorization" unless headers["AUTHORIZATION"] | ||
raise "not bearer" unless headers["AUTHORIZATION"].split[0] == "Bearer" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please define new error class.
https://github.com/ota42y/openapi_parser/blob/master/lib/openapi_parser/errors.rb
end | ||
|
||
def validate_security(security_schemes, headers) | ||
operation_object.validate_security_schemes(security_schemes, headers) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The OpenAPI Specification define that it is OK if either one of OpenAPI Object or Operation Object's security requirement is satisfied, so need to check both security fields.
https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#securityRequirementObject
When a list of Security Requirement Objects is defined on the OpenAPI Object or Operation Object, only one of the Security Requirement Objects in the list needs to be satisfied to authorize the request.
Now RequestOperation Object doesn't have OpenAPI's security field so we need pass it too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is good to create validator which require Security Scheme Object and Security Requriement Object and validate seurity schemes specified by requirement objects for validating OpenAPI Object and Operation Object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ota42y I don't understand this part:
I think it is good to create validator which require Security Scheme Object and Security Requriement Object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you are adding the validation method to the Security Requriement Object, that's fine. I was thinking of using Security Scheme Object for validation and thought that would be a problem. Ignore it.
@@ -30,5 +30,21 @@ def validate_request_body(content_type, params, options) | |||
def validate_response(response_body, response_validate_options) | |||
responses&.validate(response_body, response_validate_options) | |||
end | |||
|
|||
def validate_security_schemes(securitySchemes, headers) | |||
securitySchemes&.each do |securityScheme| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We must checks for a Security Scheme Object with a corresponding name if there is a SecurityRequriment Object so we don't need check all securitySchemes.
def validate_security_schemes(securitySchemes, headers)
validate_results = security.map do |s| # s is security requirement object
# check all security
s.validate_security_schemes(securitySchemes, headers)
end
if validate_results.count(true) == 1
return true # accept
end
return ValidateSecurityError()
end
I have determined that the validate passes if only one security scheme is valid, and if two or more are valid, the validate does not pass.
( We need check validate_results.count(true) == 1
, not validate_results.count(true) >= 1
)
Is this OK? My English is not very good, so I doubt if I am reading it correctly.
Only one of the security requirement objects need to be satisfied to authorize a request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ota42y
your english is ok...! I have to understand, security requirement object does not have schema information about type, scheme and bearerFormat is only in SecuritySchema
object.
So this code below cannot be used in security.rb
which is security requirements object (the latest code that i just pushed)
# this code cannot be used in security.rb (security requirement object)
def validate_security_schemes(securityScheme, headers)
if self.type == "http" && self.scheme == "bearer" && self.bearer_format == "JWT"
end
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ota42y Hi. any updates? I'm thinking of doing my own fork because i cannot find the solution with:
def validate_security_schemes(securitySchemes, headers)
validate_results = security.map do |s| # s is security requirement object
# check all security
s.validate_security_schemes(securitySchemes, headers) <-- this needs to be object, cannot use "self" with `securitySchemes
end
if validate_results.count(true) == 1
return true # accept
end
return ValidateSecurityError()
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
security requirement object does not have schema information about type, scheme and bearerFormat is only in SecuritySchema object.
You are right. I confused security scheme object and secuirty requirement object 🙇
@@ -65,6 +70,7 @@ def validate_response_body(response_body, response_validate_options = nil) | |||
# @param [OpenAPIParser::SchemaValidator::Options] options request validator options | |||
def validate_request_parameter(params, headers, options = nil) | |||
options ||= config.request_validator_options | |||
validate_security(security_schemes, headers) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now users are using code under the assumption that this validation will not take place, and it may suddenly break at update time.
Therefore, it would be better to control this optionally or make it a separate method and have it called separately.
# check if the JWT token is being sent and try to decode. | ||
# if JWT token does not exist or token cannot decode, then deny access | ||
token = headers["AUTHORIZATION"].split[1] | ||
value = JWT.decode token, nil, false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How much detail to verify here is a difficult question.
When verifying a signed JWT, the user expects the signature to be correct.
But we must passed key and various options to the library.
The options are numerous and require the same amount of effort as creating a wrapper for the jwt library.
Also, the OpenAPI definition does not expect JWT to have detailed information about signature verification.
One solution would be to have the user pass in a callback what validation is to be performed, and this library would only call the callback.
This would allow users to, for example, check for certain values in the JWT or force disabling of the JWT for some users only.
In this case, instead of limiting the bearerFormat=JWT, I think it would be possible to pass a Security Scheme Object to the callback so that users can validate non-JWT formats as well, thus allowing for a wider range of applications.
Late reply, but I got back to you, thanks for pointing it out right! |
No description provided.