30
votes

We have spring security oauth2 based application. Every thing is working fine. But i am failed to change default token endpoint from "/oauth/token" to "/external/oauth/token".

My spring-servlet.xml

<http pattern="/external/oauth/token" create-session="stateless" 
       authentication-manager-ref="clientAuthenticationManager"
       use-expressions="true" xmlns="http://www.springframework.org/schema/security">
      <intercept-url pattern="/external/oauth/token" access="isFullyAuthenticated()" />
      <anonymous enabled="false" />
      <http-basic entry-point-ref="clientAuthenticationEntryPoint" />
      <!-- include this only if you need to authenticate clients via request parameters -->
      <custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER" />
      <access-denied-handler ref="oauthAccessDeniedHandler"/>
</http>

<oauth:authorization-server client-details-service-ref="clientDetails" 
        token-services-ref="tokenServices" 
        user-approval-handler-ref="userApprovalHandler" token-endpoint-url="/external/oauth/token">
        <oauth:authorization-code />
        <oauth:implicit />
        <oauth:refresh-token />
        <oauth:client-credentials />
        <oauth:password />
</oauth:authorization-server>

But the result when i access this endpoint is

{
    error: "unauthorized"
    error_description: "An Authentication object was not found in the SecurityContext"
}

am i missing any thing ? Please suggest.

4

4 Answers

44
votes

With the version 2.0.5.RELEASE or above of spring-security-oauth2

In one line in java based configuration, tested and works fine, somehow it's overriding the RequestMapping value of the TokenEndpoint class.

@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {      

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints
                .pathMapping("/oauth/token", "<your custom endpoint>")
        }
}
6
votes

Just struggled with this for a few days, but have it working now on latest Spring Oauth2 1.0.5.RELEASE. I'm not 100% sure my solution is the classiest (Step 4 in particular), but it works and I'm able to move forward.

In my case, I wanted to remove the /oauth prefix from the urls to end up with just /token and /authorize. The solution for me was mostly xml config, with two hacks to override endpoint request mappings.

1 - In app context xml, add authorization-endpoint-url and token-endpoint-url attribs to your <oauth:authorization-server> element.

Mine:

<oauth:authorization-server client-details-service-ref="clientDetailsService" token-services-ref="tokenServices" user-approval-handler-ref="userApprovalHandler" authorization-endpoint-url="/authorize" token-endpoint-url="/token">

2 - In app context xml, adjust the security endpoints accordingly. There should be two, which respectively manage security on the token and auth urls. Need to update the pattern prop on <http> and <intercept-url> tags.

Mine:

<http pattern="/token/**" create-session="stateless" authentication-manager-ref="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
    <intercept-url pattern="/token/**" access="IS_AUTHENTICATED_FULLY" />

...

<http pattern="/authorize/**" access-denied-page="/login.jsp?authorization_error=true" disable-url-rewriting="true" xmlns="http://www.springframework.org/schema/security">
    <intercept-url pattern="/authorize/**" access="IS_AUTHENTICATED_FULLY" />

3 - (If you chose to employ the optional clientCreds filter.) In app context xml, you should already have wired-in the clientCredentialsTokenEndpointFilter bean as a <custom-filter> within yourelement. So, within the filter's bean, add afilterProcessesUrl` property.

Mine:

<bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
    <property name="authenticationManager" ref="clientAuthenticationManager" />
    <property name="filterProcessesUrl" value="/token" />
</bean>

4 - The last step is to override the request mapping urls of the actual internal endpoint controllers. The spring oauth2 lib comes with two classes: AuthorizationEndpoint and TokenEndpoint. Each use @RequestMapping type annotations to bind the url (as we all do for our projects' app controllers). For me, it was a hair-pulling effort to attempt to override the value of the request mappings in any way other than to (sadly) recreate the spring class package in my src folder, copy the AuthorizationEndpoint and TokenEndpoint classes verbatim into said folder, and edit the inline @RequestMapping annotation values.

Anyway, that does the trick. Would love to hear of a more graceful way to override the endpoint controller request mapping values.

Thanks.

Final, working app context:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:sec="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2"

  xsi:schemaLocation="
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
    http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd
  "
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>

  <!-- Declare OAuth2 services white-list. (This is the top of the config.) -->
  <oauth:authorization-server client-details-service-ref="clientDetailsService" token-services-ref="tokenServices" user-approval-handler-ref="userApprovalHandler" authorization-endpoint-url="/authorize" token-endpoint-url="/token">
    <oauth:authorization-code />
    <oauth:implicit />
    <oauth:refresh-token />
    <oauth:client-credentials />
    <!-- <oauth:password /> -->
  </oauth:authorization-server>
  <bean id="userApprovalHandler" class="org.springframework.security.oauth2.provider.approval.TokenServicesUserApprovalHandler">
    <!-- This bean bridges client auth service and user tokens... kind of an out of place requirement. -->
    <property name="tokenServices" ref="tokenServices" />
  </bean>

  <!-- This starts the far back-end config for client token management. -->
  <sec:authentication-manager id="clientAuthenticationManager">
    <sec:authentication-provider user-service-ref="clientDetailsUserService" />
  </sec:authentication-manager>
  <bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
    <constructor-arg ref="clientDetailsService" />
  </bean>
  <bean id="clientDetailsService" class="com.mycompany.oauth.spring.security.oauth2.IntegratedOauth2ClientDetailsService">
    <!-- This bean is what wires OAuth2 into the persistence stack for client details stored in the oauth_client table. -->
  </bean>


  <!-- OAuth is layered on to spring security which is centered around users which requires a user auth manager.  -->
  <authentication-manager alias="authenticationManager" xmlns="http://www.springframework.org/schema/security">
    <authentication-provider ref="daoAuthenticationProvider" />
  </authentication-manager>
  <bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
    <property name="userDetailsService" ref="userDetailsService" />
  </bean>

  <bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
    <property name="tokenStore" ref="tokenStore" />
    <property name="supportRefreshToken" value="true" />
    <property name="clientDetailsService" ref="clientDetailsService" />
  </bean>
  <bean id="tokenStore" class="com.mycompany.oauth.spring.security.oauth2.IntegratedOAuth2TokenStore">
    <!-- This bean is what wires OAuth2 tokens into my company's application stack. -->
    <constructor-arg ref="dataSource" />
  </bean>

  <!-- **************************************************************************************** -->
  <!-- Finally, sew OAuth into spring security with some http tags... -->
  <!-- **************************************************************************************** -->

  <!-- The OAuth2 endpoint for direct token requests (i.e. for client_credentials flow). -->
  <http pattern="/token/**" create-session="stateless" authentication-manager-ref="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
    <intercept-url pattern="/token/**" access="IS_AUTHENTICATED_FULLY" />
    <anonymous enabled="false" />
    <http-basic entry-point-ref="clientAuthenticationEntryPoint" />
    <custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
    <access-denied-handler ref="oauthAccessDeniedHandler" />
  </http>
  <bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
    <property name="authenticationManager" ref="clientAuthenticationManager" />
    <property name="filterProcessesUrl" value="/token" />
  </bean>
  <bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    <property name="realmName" value="myrealm" />
  </bean>
  <bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />

  <!-- The OAuth2 endpoint for user-approved authorization (i.e. for "authorization" flow involving user login/approve). -->
  <http pattern="/authorize/**" access-denied-page="/login.jsp?authorization_error=true" disable-url-rewriting="true" xmlns="http://www.springframework.org/schema/security">
    <intercept-url pattern="/authorize/**" access="IS_AUTHENTICATED_FULLY" />
    <form-login authentication-failure-url="/login.jsp?authentication_error=true" default-target-url="http://www.mycompany.com/" login-page="/login.jsp" login-processing-url="/login.do" />
    <http-basic />
    <anonymous />
  </http>

</beans>
5
votes

For customize the token end point URL, do the following steps.

1) Write your own class that extends ClientCredentialsTokenEndpointFilter class & call ClientCredentialsTokenEndpointFilter class constructor with "/external/oauth/token" value.

super("/external/oauth/token");

2) Plug your new customize filter in security configuration.

Replace

<custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER" />

with

<custom-filter ref="your customize filter" after="BASIC_AUTH_FILTER" />

3) Create your own class for new mapping (/external/oauth/token) & extend tokenendpoint.

4) Change http & intercept-url element's pattern attribute value to "/external/oauth/token"

4
votes

You are making this harder than what it should be, it's actually very simple ! (Notice I'm using "oauth2:" instead of "oauth:" as the XML tag)

  1. Go to your security-context.xml

  2. Find "oauth2:authorization-server" in the above file.

    <oauth2:authorization-server
                    client-details-service-ref="someService"
                    request-validator-ref="someScopeRequestValidator"
                    token-services-ref="someTokenServices" >
    
  3. Just add token-endpoint-url="/oauth/whatever_you_like"

    <oauth2:authorization-server
                    client-details-service-ref="someService"
                    request-validator-ref="someScopeRequestValidator"
                    token-services-ref="someTokenServices" 
    **token-endpoint-url="/oauth/whatever_you_like"** >