-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
Provide support for OAuth 2.0 Login #4257
Conversation
General
spring-security-oauth2-core
spring-security-oauth2-client
oauth2login
|
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'm submitting this for now and will continue the review later. This way you can iterate on it.
ClientRegistrationRepository clientRegistrationRepository, | ||
AuthorizationRequestUriBuilder authorizationUriBuilder) { | ||
|
||
Assert.notNull(filterProcessingUri, "filterProcessingUri cannot be null"); |
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.
For consistency with the Spring code base...Move all assertions to the top and then assign. Do not do assert, assign, assert, assign
|
||
@Override | ||
public final void afterPropertiesSet() { | ||
Assert.notEmpty(this.clientRegistrationRepository.getRegistrations(), "clientRegistrationRepository cannot be empty"); |
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.
Generally, try to avoid afterProperties set unless you have to because validation only happens if people remember to invoke this method. Have the clientRegistrationRepository constructor validate that it has registrations and remove this method.
* | ||
* @author Joe Grandja | ||
*/ | ||
public class AuthorizationRequestRedirectFilter extends OncePerRequestFilter { |
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.
Rather than all the final methods make the whole class final
import org.springframework.security.core.Authentication; | ||
|
||
/** | ||
* Root exception for all OAuth2-related general errors. |
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.
As explained in the note this isn't really true. The name of the class needs refined too
@@ -0,0 +1,41 @@ | |||
/* |
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 only place this is used is within a private method. I think this should be removed
try { | ||
redirectUri = this.authorizationUriBuilder.build(authorizationRequestAttributes); | ||
} catch (URISyntaxException ex) { | ||
logger.error("An error occurred building the Authorization Request: " + ex.getMessage(), ex); |
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.
Do not swallow exceptions. If this code is kept, the user would just get an IllegalArgumentException on 134 but wouldn't know it was due to the URISyntaxException. Instead we should let a RuntimeException be thrown. See comment on the builder api too
response.sendError(HttpServletResponse.SC_BAD_REQUEST, failed.getMessage()); | ||
} | ||
|
||
private String normalizeUri(String uri) { |
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.
Can you use UriComponentsBuilder instead of writing the logic yourself?
/** | ||
* @author Joe Grandja | ||
*/ | ||
public interface AuthorizationGrantTokenExchanger<T extends AuthorizationGrantAuthenticationToken> { |
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.
Is there a reason this is generic vs just always using AuthorizationCodeGrantAuthenticationToken
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.
There will be different implementations of AuthorizationGrantTokenExchanger
for each of the grant types authorization_code
, client_credentials
and resource_owner_password
grant types.
For example, the client_credentials
grant type would be ClientCredentialsTokenExchanger
which would be associated with it's specific type of AuthorizationGrantAuthenticationToken
, for example, ClientCredentialsAuthenticationToken
/** | ||
* @author Joe Grandja | ||
*/ | ||
public interface AuthorizationRequestUriBuilder { |
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.
This API be combined with AuthorizationGrantTokenExchanger
?
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 don't feel we should combine these 2 API's.
AuthorizationRequestUriBuilder
is used for authorization_code
and implicit
grant types whereas AuthorizationGrantTokenExchanger
is used for authorization_code
, client_credentials
and resource_owner_password
grant types.
We also have only one default implementation for AuthorizationRequestUriBuilder
that will serve for authorization_code
and implicit
grant types. We will need different implementations of AuthorizationGrantTokenExchanger
for each of authorization_code
, client_credentials
and resource_owner_password
grant types.
/** | ||
* @author Joe Grandja | ||
*/ | ||
public abstract class AuthorizationGrantAuthenticationToken extends AbstractAuthenticationToken { |
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 don't need this abstract class. We can extract it later if necessary. For now move everything into AuthorizationCodeGrantAuthenticationToken
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.
See comment
367f7e9
to
dfca383
Compare
@jgrandja @rwinch Stupid question but is not actually the goal of I mean with generic |
Hi @kakawait . As mentioned in #3907, OAuth-specific code is spread out in a few projects so we are looking to provide a uniform lower-level base that all projects can leverage. The goal is to have the other projects, for example Spring Social, leverage the lower-level protocol flow logic that will be provided within Spring Security. |
@jgrandja Great news, because I had the same issue when I wanted to add social login on my own UAA implementation, I found multiple way to achieve:
I personally choose Thus I think I will not plan to support an uniform lower-level base for OAuth 1? I'm ok with that since OAuth 1 is dying. |
@rwinch All requested changes have been completed except for the following. When I login using my GitHub client, I am able to see the I still need to look at the STS/Buildship issue. I'll work on the README for the sample next. |
@rwinch I think the URI naming convention you suggested makes sense.
|
|
google: | ||
client-id: your-app-client-id | ||
client-secret: your-app-client-secret | ||
redirect-uri: http://localhost:8080/oauth2/authorize/code/google |
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 should be able to default the redirect URI.
We know the host at the time the request comes in. We also know the client alias at this time (it is used to look up the details).
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.
Resolved via 4d94134
} | ||
} | ||
|
||
private static class OAuth2ClientCondition extends SpringBootCondition implements ConfigurationCondition { |
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 you can just remove this and it's subclasses and use @ConditionalOnProperty
directly on the configuration.
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.
Resolved via 68f4891
public class OktaClientProperties extends ClientRegistrationProperties { | ||
public static final String CLIENT_NAME = "Okta"; | ||
public static final String CLIENT_ALIAS = "okta"; | ||
public static final String AUTHORIZATION_URI = "https://your-subdomain.oktapreview.com/oauth2/v1/authorize"; |
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.
Only default values that reduce the amount of configuration the user needs to provide. Defaults that are incorrect should be removed.
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.
Resolved via 68f4891
@Configuration | ||
@ConditionalOnWebApplication | ||
@ConditionalOnClass(ClientRegistration.class) | ||
public class ClientRegistrationAutoConfiguration { |
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'd like to see this support creating a ClientRegistration
from arbitrary client aliases rather than just specific aliases. The client aliases with a convention (i.e. google
) simply provide defaults. The client aliases that are unknown the user should still only need to provide environment entries (no need to create the bean)
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.
Resolved via 68f4891
* @author Joe Grandja | ||
*/ | ||
@EnableWebSecurity | ||
public class SecurityConfig extends WebSecurityConfigurerAdapter { |
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.
SecurityConfig
should not be required. Instead we should allow the user to provide the java class and URL to map the user info in the environment.
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.
Resolved via 8244855
@rwinch In reference to your comment...
It looks like your email address is set as private.
|
@rwinch I changed the member types from |
a6cd297
to
27d0c45
Compare
/** | ||
* @author Joe Grandja | ||
*/ | ||
public final class ResponseAttributesExtractor { |
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 don't think this should be static classes. Instead perhaps consider implementing the Converter (or similar) API.
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.
Resolved via b876eda
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.springframework.security.oauth2.core.protocol; |
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 don't think this belongs in the core protocol package. There will be a reactive implementation of the OAuth support that does not support HttpServletRequest. Rather I think we should create a new package that contains converters used for the web
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.
Resolved via b876eda
It would be good to also place the oauth2 stuff in a folder so all oauth2 stuff is within it. |
private final String uri; | ||
private final HttpStatus statusCode; | ||
|
||
public enum ErrorCode { |
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 struggle with this being an Enum because I don't think we will ever be able to guarantee all the errors are enumerated (which is what an Enum is). We could potentially allow OAuth2Error to accept an ErrorCode interface and then create some constants for well known errors
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.
Resolved via 55f15f6
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) | ||
throws AuthenticationException, IOException, ServletException { | ||
|
||
if (this.isAuthorizationCodeErrorResponse(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.
Extract to leverage a converter API and if it converts to OAuth2Error then throw exception
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.
Resolved via b876eda
7105b99
to
3785a2f
Compare
5e47a7a
to
1cba5eb
Compare
1cba5eb
to
829c386
Compare
This PR provides support for authenticating using an external OAuth 2.0 or OpenID Connect 1.0 Provider. For example, login using Google, Facebook, GitHub, etc.
This PR addresses #3907