This is issue is present because the /profile/applications route points to Oauth::ApplicationsController, which inherits from Doorkeeper::ApplicationsController instead of
inheriting from Profiles::ApplicationController or including EnforcesTwoFactorAuthentication. Because of this this controller may be vulnerable to other authorisation issues.
Juan Broullonchanged title from {-Access control - "Enforcing 2FA for all users" in one group on https://gitlab.com/-} to 2FA not enforced on /profile/applications
changed title from {-Access control - "Enforcing 2FA for all users" in one group on https://gitlab.com/-} to 2FA not enforced on /profile/applications
@jeremymatos@mksionek hmm does that mean we should fix them both in the same MR (or at least, the same security release) to avoid disclosing details?
I saw we also have routes for the Doorkeeper::TokensController which we don't override at all in our app. AFAIK the create action there can also be used to generate access tokens with some of the other OAuth flows.
For reference, here are all the routes defined by the doorkeeper gem:
native_oauth_authorization GET /oauth/authorize/native(.:format) oauth/authorizations#show oauth_authorization GET /oauth/authorize(.:format) oauth/authorizations#new DELETE /oauth/authorize(.:format) oauth/authorizations#destroy POST /oauth/authorize(.:format) oauth/authorizations#create oauth_token POST /oauth/token(.:format) doorkeeper/tokens#create oauth_revoke POST /oauth/revoke(.:format) doorkeeper/tokens#revoke oauth_introspect POST /oauth/introspect(.:format) doorkeeper/tokens#introspect oauth_applications GET /oauth/applications(.:format) oauth/applications#index POST /oauth/applications(.:format) oauth/applications#create new_oauth_application GET /oauth/applications/new(.:format) oauth/applications#new edit_oauth_application GET /oauth/applications/:id/edit(.:format) oauth/applications#edit oauth_application GET /oauth/applications/:id(.:format) oauth/applications#show PATCH /oauth/applications/:id(.:format) oauth/applications#update PUT /oauth/applications/:id(.:format) oauth/applications#update DELETE /oauth/applications/:id(.:format) oauth/applications#destroyoauth_authorized_applications GET /oauth/authorized_applications(.:format) oauth/authorized_applications#index oauth_authorized_application DELETE /oauth/authorized_applications/:id(.:format) oauth/authorized_applications#destroy oauth_token_info GET /oauth/token/info(.:format) oauth/token_info#show
Most of these actions sound like they need a 2FA check, but I'm unsure about these:
oauth/token_info#show
doorkeeper/tokens#introspect
The doorkeeper-openid_connect gem also adds these routes:
oauth_userinfo GET /oauth/userinfo(.:format) doorkeeper/openid_connect/userinfo#show POST /oauth/userinfo(.:format) doorkeeper/openid_connect/userinfo#showoauth_discovery_keys GET /oauth/discovery/keys(.:format) doorkeeper/openid_connect/discovery#keys
The discovery endpoint is public and should be fine, and the userinfo endpoints only work with a valid access token so it sounds similar to the token_info and introspect endpoints from Doorkeeper.
@toupeira Yes it is recommended to fix it in the same MR. 2 different MRs in the same security release would work also, but it could cause merge conflicts.
I don't know enough the usage of each route to know if 2FA should be checked on all of those, but yes the safe choice is by default to protect all of them with 2FA. And only deactivate it for the routes that should not have 2FA.
I'm looping in @dblessing because he recently had to look for OAuth routes when fixing another security issue.
In that case I think we can just add EnforcesTwoFactorAuthentication to that base controller.
And I see now they later also added a base_metal_controller setting in https://github.com/doorkeeper-gem/doorkeeper/pull/1283, so maybe we can just specify the same Gitlab::BaseDoorkeeperController there? Not really familiar with the metal controller stuff in Rails, but AFAIK it's just a performance optimization if you don't want to use the full Rails stack?
Or I guess we could also just create a Gitlab::BaseMetalDoorkeeperController.
I don't know enough the usage of each route to know if 2FA should be checked on all of those, but yes the safe choice is by default to protect all of them with 2FA. And only deactivate it for the routes that should not have 2FA. I'm looping in @dblessing because he recently had to look for OAuth routes when fixing another security issue.
There might still be some edge cases, but as it would only affect users with a 2FA requirement that don't have 2FA enabled, it's probably not really a big deal.
@toupeira one more time I need to pick your brain.
Both of this controllers: Oauth::TokenInfoController and Doorkeeper::TokensController inherit from Doorkeeper::ApplicationMetalController, which inherits from ActionController::API. We cannot use helper_method then, which is used in concern enforcing 2FA. Since both of this controllers are api-only, maybe we need to enforce 2FA in a different way?
@mksionek hmm check_two_factor_requirement only does a redirect which seems to be supported in metal controllers, and it looks like the helper methods are only used in app/views/profiles/two_factor_auths/show.html.haml which I don't think we need here.
So I think the easiest option would be to wrap that helper_method call in a if defined?(:helper_method) block, with a comment mentioning that this is needed to support ActionController::Metal.
Another approach might be to return a JSON response instead of doing a redirect, but personally I don't think that's really worth it as the 302 status from the redirect should be obvious enough too.