30

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.

Tuna
  • 2,937
  • 4
  • 37
  • 61
Srikanth
  • 534
  • 1
  • 7
  • 20

4 Answers4

45

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>")
        }
}
Emilien Brigand
  • 9,943
  • 8
  • 32
  • 37
  • Hi @Emilien Brigand I tried this to change endpoints.pathMapping("/oauth/authorize", "/external/oauth/authorize"); but it is not going to my custom action mapping defined in custom controller. Can you please suggest solution? – Rajeev Gupta Jan 09 '17 at 01:03
  • just in case, did you put the path of your endpoint instead of "/external/oauth/authorize"? – Emilien Brigand Jan 12 '17 at 15:10
  • Hello @Emilien Brigand thanks for your reply. Can you please tell me which path of endpoint you are talking about? – Rajeev Gupta Jan 13 '17 at 09:40
  • I m talking about your custom controller, a controller define a REST endpoint, so you just need to put the URL of it – Emilien Brigand Jan 13 '17 at 10:27
  • Hi @Emilien Brigand thanks for your reply. You mean to say if my controller endpoint is http://localhost:8080/api//external/oauth/token then I need to give mapping this way: endpoints .pathMapping("/oauth/token", "/api/external/oauth/token") This mapping, you mean to say? – Rajeev Gupta Jan 17 '17 at 09:50
  • it worked for me i did as endpoints.pathMapping("/oauth/token", "/v1/oauth/token"); – faisalbhagat Feb 02 '17 at 11:08
  • @RajeevGupta I changed the response for more clarity, but yes to answer to your question – Emilien Brigand Feb 02 '17 at 11:58
  • is it possible to have multiple custom endpoint for `/oauth/token`. As per `AuthorizationServerEndpointsConfigurer#pathMapping()` maps custom url to map, so I guess its not possible via this way. Should I implement an interceptor for this or how should I go ahead with this ? – balboa_21 Mar 29 '17 at 13:08
6

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>
Steven Francolla
  • 377
  • 6
  • 14
  • please suggest me, what is `realmName` in `clientAuthenticationEntryPoint`. I also face the same error, but not cusomize the default token url. I am using spring-rest service with for Oauth. – Harmeet Singh Taara Jul 24 '14 at 08:15
5

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"

Anurag
  • 137
  • 7
  • Hi Anurag, i have added all these line and given my end pointer reference to tag. But on server start up it is throwing an Exception "java.lang.IllegalArgumentException: authenticationManager must be specified". Any thing else need to be done here?.. Thanks – Srikanth Apr 03 '14 at 10:52
  • you need to set authenticationManager bean in your clientCredentialsTokenEndpoint filter – Imrank Oct 30 '15 at 05:05
4

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"** >
    
Vikdor
  • 23,934
  • 10
  • 61
  • 84
grepit
  • 21,260
  • 6
  • 105
  • 81
  • I m trying to change the token-end point url as you said to '/api/v1/oauth/token' but its giving error -`An Authentication object was not found in the SecurityContext`. Am I missing something? – Anita Jun 11 '15 at 10:00
  • This solution helped me inject custom OAuth2RequestValidator into TokenEndpoint via xml config. – Raf Feb 24 '17 at 15:37