How to set the body on an apache camel http POST request - oauth-2.0

I am simply trying to send credentials to a restful service that takes in a json formatted username and password and will return an access token. No matter what I try, I get a 400 error and the error returned from the server is "Must supply a body." Here's the last snippet I tried:
#Component
public class LoginRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
String jsonCredentials = "{\"username\":\"username\",\"password\":\"password\",\"grant_type\":\"password\",\"scope\":\"admin\"}";
from("timer://login?repeatCount=1")
.setBody(constant(jsonCredentials))
.setHeader(Exchange.HTTP_METHOD, constant(org.apache.camel.component.http4.HttpMethods.POST))
.to("http://URL");
}
}
I have confirmed my credentials work fine in Postman and receive the proper response, with access token. I believe the service I am trying to connect to is using Oauth2.

Try marshalling. For me this worked:
// your custom defined bean
Credentials creds = new Credentials(username, password, ...);
from(...).setBody(creds).marshal().json(JsonLibrary.Jackson).to(...

Related

SpringSecurity UserDetailsService REST Client

I'm using SpringBoot 2.4.7 and I'm trying to implement jdbc Authentication. The problem is that I can't reach the backend via http. I have read that with following configuration:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login")
.permitAll()
.and()
.formLogin()
....
I can reach a default login page at my context application address. But I would like to call a POST login endpoint with username and password parameters.
How can I do this?
If you are trying to receive the user credentials via a REST Endpoint and manually authenticate the user you can do this way:
#RestController
#RequestMapping("/login")
public class LoginController {
private final AuthenticationManager authenticationManager;
// constructor injecting authenticationManager
#PostMapping
public void login(#RequestBody UserCredentials credentials) {
UsernamePasswordAuthenticationToken token
= new UsernamePasswordAuthenticationToken(credentials.getUsername(), credentials.getPassword());
Authentication auth = this.authenticationManager.authenticate(token);
if (auth != null) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(auth);
SecurityContextHolder.setContext(context);
}
throw new SomeException();
}
}
This way, the Filters will take care of the rest of the authentication steps for you. The Spring Security documentation can be researched for more details.
If you want to use the endpoint generated with the default login page, you can follow the steps from the documentation to make your own request:
The form should perform a post to /login
The form will need to
include a CSRF Token which is automatically included by Thymeleaf.
The form should specify the username in a parameter named username
The form should specify the password in a parameter named password
If the HTTP parameter error is found, it indicates the user failed to
provide a valid username / password
If the HTTP parameter logout is
found, it indicates the user has logged out successfully

Unable to call nuxeo rest api from rest client when integrated with Keycloak authentication plugin

I wanted to integrate keycloak as authentication plugin for nuxeo platform both running on my local machine
Set up details
Nuxeo platform version: 10.10 (runs on tomcat 9)
Keycloak version: 6.0.1
keycloak tomcat adapter distribution: keycloak-tomcat8-adapter-dist
I followed the steps mentioned in link https://github.com/nuxeo/nuxeo/tree/master/nuxeo-services/login/nuxeo-platform-login-keycloak.
Here, I built the nuxeo-platform-login-keycloak plugin for keycloak 6.0.1 version.
On keycloak,
I set up a auth client under newly created realm 'demo'
Client details available in
client configuration
I created role as 'Members' and added admin role to it
I created a user 'keycloakuser' and added to 'Members'.
When nuxeo ui is hit from browser, the authentication flow works fine. It redirects me to login page of keycloak, on valid credentials, it redirects me to nuxeo ui.
The user created along with 'Members' group assigned to it.
Error Scenario
To call rest api from postman, I configured Oauth2 for authentication.
Auth url: http://localhost:8080/auth/realms/demo/protocol/openid-connect/auth
Token Url: http://localhost:8080/auth/realms/demo/protocol/openid-connect/token
Client: testclient
Client secret: *****
Scope: openid
I used access_token obtained using Oauth2 flow, to make API call as http://localhost:8190/nuxeo/api/v1/id/document_id. It is failing with
java.lang.ClassCastException: class org.apache.catalina.core.ApplicationHttpRequest cannot be cast to class org.apache.catalina.connector.RequestFacade (org.apache.catalina.core.ApplicationHttpRequest and org.apache.catalina.connector.RequestFacade are in unnamed module of loader java.net.URLClassLoader #39aeed2f)
at org.nuxeo.ecm.platform.ui.web.keycloak.DeploymentResult.invokeOn(DeploymentResult.java:79) [nuxeo-platform-login-keycloak-10.10.jar:?]
at org.nuxeo.ecm.platform.ui.web.keycloak.KeycloakAuthenticatorProvider.provide(KeycloakAuthenticatorProvider.java:56) [nuxeo-platform-login-keycloak-10.10.jar:?]
at org.nuxeo.ecm.platform.ui.web.keycloak.KeycloakAuthenticationPlugin.handleRetrieveIdentity(KeycloakAuthenticationPlugin.java:113) [nuxeo-platform-login-keycloak-10.10.jar:?]
at org.nuxeo.ecm.platform.ui.web.auth.NuxeoAuthenticationFilter.handleRetrieveIdentity(NuxeoAuthenticationFilter.java:1137) [nuxeo-platform-web-common-10.10.jar:?]
at org.nuxeo.ecm.platform.ui.web.auth.NuxeoAuthenticationFilter.doFilterInternal(NuxeoAuthenticationFilter.java:548) [nuxeo-platform-web-common-10.10.jar:?]
Observation:
1. The API request call is not hitting the keycloak endpoint
2. I tried to print the reqqest type (actually the request wrapper type) in both scenarios.
For browser request, it was org.apache.catalina.connector.RequestFacade and for api request it was org.apache.catalina.core.ApplicationHttpRequest which is not extending org.apache.catalina.connector.RequestFacade
Questions:
1. Does above behavior (mentioend in point 2) differ in tomcat versions earlier to tomcat 9?
2. Is the problem with compatibility issues with tomcat version and keycloak adapters jar version?
Late answer, but it may come handy for new readers. I had the exact same issue some months ago. It seems to be due to a bug in the nuxeo-platform-login-keycloak plugin.
I endend making the following changes to org.nuxeo.ecm.platform.ui.web.keycloak.DeploymentResult:
public class DeploymentResult {
final static Logger LOGGER = LoggerFactory.getLogger(DeploymentResult.class);
private boolean isOk;
private static KeycloakDeployment keycloakDeployment;
private HttpServletRequest httpServletRequest;
private HttpServletResponse httpServletResponse;
private Request request;
private CatalinaHttpFacade facade;
public DeploymentResult(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
this.httpServletRequest = httpServletRequest;
this.httpServletResponse = httpServletResponse;
}
boolean isOk() {
return isOk;
}
public static KeycloakDeployment getKeycloakDeployment() {
return keycloakDeployment;
}
public Request getRequest() {
return request;
}
public CatalinaHttpFacade getFacade() {
return facade;
}
public DeploymentResult invokeOn(AdapterDeploymentContext deploymentContext) {
// In Tomcat, a HttpServletRequest and a HttpServletResponse are wrapped in a Facades
if (httpServletRequest instanceof RequestFacade) {
// Received upon logout.
request = unwrapRequest(httpServletRequest);
} else {
request = unwrapRequest(((ServletRequestWrapper) httpServletRequest).getRequest());
}
facade = new CatalinaHttpFacade(httpServletResponse, request);
if (keycloakDeployment == null) {
keycloakDeployment = deploymentContext.resolveDeployment(facade);
}
if (keycloakDeployment.isConfigured()) {
isOk = true;
return this;
}
isOk = false;
return this;
}
/**
* Get the wrapper {#link Request} hidden in a {#link ServletRequest} object
*
* #param servletRequest, the main ServletRequest object
* #return the wrapper {#link Request} in {#link ServletRequest}
*/
private Request unwrapRequest(final ServletRequest servletRequest) {
try {
final Field f = servletRequest.getClass().getDeclaredField("request");
f.setAccessible(true); // grant access to (protected) field
return (Request) f.get(servletRequest);
} catch (final NoSuchFieldException | IllegalAccessException e) {
LOGGER.error("Couldn't unwrap request", e);
throw new RuntimeException(e);
} catch (final Exception e) {
LOGGER.error("Couldn't unwrap request", e);
throw e;
}
}
}
After building and deploying the plugin with these changes, I was allowed to call Nuxeo's REST API with bearer authentication using access token obtained through Keycloak.
Disclaimer: I focused on making it work, not on making it clean...

Is there a way to change http request method in netflix zuul routing filter?

I'm trying to trasform http GET method call from legacy api server built with MVC1 pattern to new restful api server without any change of front-end source code using netflix zuul and eureka.
I added zuul pre filter transforming legacy url to restful convention url working after PreDecorationFilter and it works fine.
But now I'm facing problem converting the GET method to proper method like POST, PUT, DELETE by distinguising url so that the requests are properly mapped in spring controller via #GetMapping/#PostMapping/#PutMapping/#DeleteMapping.
I looked into SimpleRoutingFilter that handles HttpClient but
Because of environmental constraint, I have to use eureka service id to route to the new api server and that means I should use RibbonRoutingFilter which is quite complicated to find out a right place to this operation in.
So, is this possible to change http method or make new http request before RibbonRoutingFilter?
If possible can you please suggest where is the right place to do that or some reference?
Many thanks!
======================================================================
Milenko Jevremovic,
Would you please tell me more detail about using Feign?
I defiend #FeignClient like below
#PostMapping(value = "{url"}, consumes = "application/json")
ResponseEntity<?> postMethod(#PathVariable("url") String url);
and to get query parameters to request body for POST In zuul pre filter,
after transform logic from GET request url to POST new restful url ...
byte[] bytes = objectMapper.writeValueAsBytes(ctx.get("requestQueryParams"));
ctx.setRequests(new HttpServletRequestWrapper(request) {
#Override ..getMethod
#Override ..getContentLength
#Override ..getConentLengthLong
#Override
public ServletInputStream getInputStream() {
return new ServletInputStreamWrapper(bytes);
}
}
ResponseEntity<?> response feignClient.post(transformedNewApiUri);
and set RequestContext code that you suggested ....
and controller of new api server is like,
#PostMapping
ResponseEntity<model> post(#RequestBody req..)
It comes to controller fine but when I see the http request in post method of controller,
There is no request body for parameters.
(HttpServleterRequest getInputStream shows empty)
The request data set in zuul pre filter by HttpServletRequestWrapper is
not used in Feign maybe...?
Would you please get me more idea setting request body when changing GET query
to POST constructor for using Feign?
It is not possible to change method of HttpServletRequest, but it's possible to replace request in RequestContext. HttpServletRequestWrapper appears to be very helpful:
static class PostHttpServletRequest extends HttpServletRequestWrapper {
public PostHttpServletRequest(HttpServletRequest request) {
super(request);
}
#Override
public String getMethod() {
return "POST";
}
}
So method run can be rewritten as following:
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
HttpServletRequest requestWrapper = new PostHttpServletRequest(request);
ctx.setRequest(requestWrapper);
return null;
}
After doing some research did not find any built in solution.
But what comes in my mind you can use Feign client in your Pre filter, get the response, set the response and return it immediately to client from your Pre filter.
You can set Feign client url or your service id, like it is explained in the docs, it uses ribbon as well .
Change response in your run method like:
...
RequestContext ctx = RequestContext.getCurrentContext();
ctx.setResponseStatusCode(your_code);
ctx.setResponseBody(new_body);
ctx.setSendZuulResponse(false);
return null

How are Web API access tokens validated on the server?

Once a WebAPI access token is generated, how does WebAPI validate that token for the next request? I wonder if I can use an [Authorize] attribute, it must compare the token sent by the client with the token at the server side, if stored somewhere. Does it just check if token is present and not its value?
Bearer token
First of all, your Identity Provider or Token Provider which issues the authorization tokens needs to have the same machine key settings as the Web Api application for encryption/decryption:
<machineKey
decryptionKey="B7EFF1C5839A624ED0268917EDE82F408D2ECBFAC817"
validation="SHA1"
validationKey="C2B8DF31AB9624D8066DFDA1A479542825F3B48865C4E47AF6A026F22D853DEC2B3248DF268599BF89EF78B9E86CA05AC73577E0D5A14C45E0267588850B"
/> </system.web>
Because under the hood Bearertoken uses MachineKey encryption.
In other words if you dont have the same settings, your web api won't be able to decrypt the token (validate it).
This is done automatically by:
Microsoft.Owin.Security.OAuth.dll
using middleware.
You can use the Authorize Attribute on your web api controllers/actions if you want simple authorization with Usernames or roles like this:
[Authorize(Roles="Administrators,Managers",Users ="Mike,Laura")]
If you want custom authorization, then you have to implement a custom authorization attribute which will handle the custom authorization in your web api. If the user is not allowed to pass you will return a 401 UnAuthorized Response:
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
actionContext.Response.Headers.Add("WWW-Authenticate","Bearer location='http://localhost:8323/account/login'");
For example:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class CustomAuthorizeAttribute : System.Web.Http.Filters.AuthorizationFilterAttribute
{
public RulesExampleEnum[] Rules { get; set; }
public string Id { get; set; }
.....
// Summary:
// Calls when a process requests authorization.
//
// Parameters:
// actionContext:
// The action context, which encapsulates information for using System.Web.Http.Filters.AuthorizationFilterAttribute.
public virtual void OnAuthorization(HttpActionContext actionContext);
public virtual Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken);
and register it in your webApiConfig.cs
config.Filters.Add(new CustomAuthorizeAttribute());
and apply it on Web Api controller or action:
[CustomAuthorize(Id = "AnyId", Rules = new RulesExampleEnum[] { RulesExampleEnum.Rule1, RulesExampleEnum.Rule3 })]
public IEnumerable<object> Get()
{...
Once access token is generated, client must include the access token inside Header for each request.
Client may set the access token inside Authorization HTTP Header.
On the server side, you should create class to handle the Authorization, which is a derived class from System.Web.Http.AuthorizeAttribute, something like below :
public class AuthorizationHandlerAttribute : AuthorizeAttribute
{
string AccessTokenFromRequest = "";
if (actionContext.Request.Headers.Authorization != null)
{
// get the access token
AccessTokenFromRequest = actionContext.Request.Headers.Authorization.Parameter;
}
string AccessTokenStored = "";
// write some code to get stored access token, probably from database
// then assign the value to a variable for later use
// compare access token
if (AccessTokenFromRequest != AccessTokenStored)
{
// if the token is not valid then return 401 Http Stasus
// or simply call base method
base.HandleUnauthorizedRequest(actionContext);
}
}
Then you use the newly created class and attach it on controller or action you wished to protect from unauthorized access.
public class UsersController : ApiController
{
[AuthorizationHandler]
public User Get(int id)
{
// only request with valid access token will reach this
}
}
The secret key is transmitted in the header of the request from the client to the server, and the contents are validated at the server at each request where the [Authorize] attribute is used.
You can use a tool like Fiddler from Telerik (free) to see the data being transported, but not the content (since its encrypted). Inspecting raw web traffic is invaluable when working with MVC / WebAPI, so I highly recommend it. Here's a link to Fiddler, although other similar tools exist as well.
http://www.telerik.com/fiddler
To answer the second part of your question, the server absolutely checks the contents of the secret key before allowing the request to proceed as authorized.

Integrate Spring Security OAuth2 and Spring Social

I'm working with a Spring Boot + Spring Security OAuth2 application that I believe was inspired by examples from Dave Syer. The application is configured to be an OAuth2 authorization server, with a single public client using the Resource Owner Password Credentials flow. A successful token is configured to be a JWT.
The public Angular client sends a POST request to /oauth/token with a basic auth header containing the client id and secret (this was the easiest way to get the client to authenticate, even though the secret is not private). The body of the request contains username, password, and grant type of "password".
In addition to being an authentication server, the application is a RESTful resource server for users, teams, and organizations.
I'm trying to add an additional SSO authentication flow using Spring Social. I've got Spring Social configured to authenticate through external providers via /auth/[provider]; however, following requests no longer have the SecurityContext correctly set. Possibly, Spring Security OAuth server or client is overriding the SecurityContext?
If I can get the SecurityContext correctly set after the Spring Social flow, I've got a new TokenGranter that allows a new grant type of "social" that would check the SecurityContextHolder for the pre authenticated user.
I'm interested in both a solution to my specific problem with the SecurityContext (I believe it's an issue with Spring OAuth + Social integration), or a different approach for authenticating with external providers and getting a valid JWT from our own auth server.
Thanks!
I had a similar problem on a JHipster-generated web application. Finally I decided to go with the SocialAuthenticationFilter option from Spring Social (via the SpringSocialConfigurer). After a successful social login, the server automatically generates and returns the "own" access token via redirection to the client app.
Here's my try:
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter implements EnvironmentAware {
//...
#Inject
private AuthorizationServerTokenServices authTokenServices;
#Override
public void configure(HttpSecurity http) throws Exception {
SpringSocialConfigurer socialCfg = new SpringSocialConfigurer();
socialCfg
.addObjectPostProcessor(new ObjectPostProcessor<SocialAuthenticationFilter>() {
#SuppressWarnings("unchecked")
public SocialAuthenticationFilter postProcess(SocialAuthenticationFilter filter){
filter.setAuthenticationSuccessHandler(
new SocialAuthenticationSuccessHandler(
authTokenServices,
YOUR_APP_CLIENT_ID
)
);
return filter;
}
});
http
//... lots of other configuration ...
.apply(socialCfg);
}
}
And the SocialAuthenticationSuccessHandler class:
public class SocialAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
public static final String REDIRECT_PATH_BASE = "/#/login";
public static final String FIELD_TOKEN = "access_token";
public static final String FIELD_EXPIRATION_SECS = "expires_in";
private final Logger log = LoggerFactory.getLogger(getClass());
private final AuthorizationServerTokenServices authTokenServices;
private final String localClientId;
public SocialAuthenticationSuccessHandler(AuthorizationServerTokenServices authTokenServices, String localClientId){
this.authTokenServices = authTokenServices;
this.localClientId = localClientId;
}
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
log.debug("Social user authenticated: " + authentication.getPrincipal() + ", generating and sending local auth");
OAuth2AccessToken oauth2Token = authTokenServices.createAccessToken(convertAuthentication(authentication)); //Automatically checks validity
String redirectUrl = new StringBuilder(REDIRECT_PATH_BASE)
.append("?").append(FIELD_TOKEN).append("=")
.append(encode(oauth2Token.getValue()))
.append("&").append(FIELD_EXPIRATION_SECS).append("=")
.append(oauth2Token.getExpiresIn())
.toString();
log.debug("Sending redirection to " + redirectUrl);
response.sendRedirect(redirectUrl);
}
private OAuth2Authentication convertAuthentication(Authentication authentication) {
OAuth2Request request = new OAuth2Request(null, localClientId, null, true, null,
null, null, null, null);
return new OAuth2Authentication(request,
//Other option: new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), "N/A", authorities)
new PreAuthenticatedAuthenticationToken(authentication.getPrincipal(), "N/A")
);
}
private String encode(String in){
String res = in;
try {
res = UriUtils.encode(in, GeneralConstants.ENCODING_UTF8);
} catch(UnsupportedEncodingException e){
log.error("ERROR: unsupported encoding: " + GeneralConstants.ENCODING_UTF8, e);
}
return res;
}
}
This way your client app will receive your web app's access token via redirection to /#/login?access_token=my_access_token&expires_in=seconds_to_expiration, as long as you set the corresponding REDIRECT_PATH_BASE in SocialAuthenticationSuccessHandler.
I hope it helps.
First, I would strongly recommend you to move away from the password grant for such a use case.
Public clients (JavaScript, installed applications) cannot keep their client secret confidential, that's why they MUST NOT be assigned one : any visitor inspecting your JavaScript code can discover the secret, and thus implement the same authentication page you have, storing your users passwords in the process.
The implicit grant has been created exactly for what you are doing.
Using a redirection-based flow has the advantage of leaving the authentication mechanism up to the authorization server, instead of having each of your applications have a piece of it : that's mostly the definition of Single Sign On (SSO).
With that said, your question is tightly related to this one I just answered : Own Spring OAuth2 server together with 3rdparty OAuth providers
To sum up the answer :
In the end, it's about how your authorization server secures the AuthorizationEndpoint : /oauth/authorize. Since your authorization server works, you already have a configuration class extending WebSecurityConfigurerAdapter that handles the security for /oauth/authorize with formLogin. That's where you need to integrate social stuff.
You simply cannot use a password grant for what you're trying to achieve, you must have your public client redirect to the authorization server. The authorization server will then redirect to the social login as its security mechanism for the /oauth/authorize endpoint.
I was starting with the good answer of above (https://stackoverflow.com/a/33963286/3351474) however with my version of Spring Security (4.2.8.RELEASE) this fails. The reason is that in org.springframework.security.access.intercept.AbstractSecurityInterceptor#authenticateIfRequired the PreAuthenticatedAuthenticationToken of the answer is not authenticated. Some GrantedAuthorities have to be passed.
In addition sharing the token in an URL parameter is not good, it should always be hidden in an HTTPs payload or header. Instead a HTML template is loaded and the token value is inserted into a ${token} placeholder field.
Here the revised version:
NOTE: The used UserDetails here is implementing org.springframework.security.core.userdetails.UserDetails
#Component
public class SocialAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
#Autowired
private OAuth2TokenStore tokenStore;
#Qualifier("tokenServices")
#Autowired
private AuthorizationServerTokenServices authTokenServices;
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
IClient user = ((SocialUserDetails) authentication.getPrincipal()).getUser();
// registration is not finished, forward the user, a marker interface
// IRegistration is used here, remove this if there no two step approach to
// create a user from a social network
if (user instanceof IRegistration) {
response.sendRedirect(subscriberRegistrationUrl + "/" + user.getId());
}
OAuth2AccessToken token = loginUser(user);
// load a HTML template from the class path and replace the token placeholder within, the HTML should contain a redirect to the actual page, but must store the token in a safe place, e.g. for preventing CSRF in the `sessionStorage` JavaScript storage.
String html = IOUtils.toString(getClass().getResourceAsStream("/html/socialLoginRedirect.html"));
html = html.replace("${token}", token.getValue());
response.getOutputStream().write(html.getBytes(StandardCharsets.UTF_8));
}
private OAuth2Authentication convertAuthentication(Authentication authentication) {
OAuth2Request request = new OAuth2Request(null, authentication.getName(),
authentication.getAuthorities(), true, null,
null, null, null, null);
// note here the passing of the authentication.getAuthorities()
return new OAuth2Authentication(request,
new PreAuthenticatedAuthenticationToken(authentication.getPrincipal(), "N/A", authentication.getAuthorities())
);
}
/**
* Logs in a user.
*/
public OAuth2AccessToken loginUser(IClient user) {
SecurityContext securityContext = SecurityContextHolder.getContext();
UserDetails userDetails = new UserDetails(user);
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, "N/A", userDetails.getAuthorities());
securityContext.setAuthentication(authentication);
OAuth2Authentication oAuth2Authentication = convertAuthentication(authentication);
// delete the token because the client id in the DB is calculated as hash of the username and client id (here also also identical to username), this would be identical to the
// to an existing user. This existing one can come from a user registration or a previous user with the same name.
// If a new entity with a different ID is used the stored token hash would differ and the the wrong token would be retrieved
tokenStore.deleteTokensForUserId(user.getUsername());
OAuth2AccessToken oAuth2AccessToken = authTokenServices.createAccessToken(oAuth2Authentication);
// the DB id of the created user is returned as additional data, can be
// removed if not needed
((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(new HashMap<>());
oAuth2AccessToken.getAdditionalInformation().put("userId", user.getId());
return oAuth2AccessToken;
}
}
Example socialLoginRedirect.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example App</title>
<meta http-equiv="Refresh" content="0; url=/index.html#/home"/>
</head>
<script>
window.sessionStorage.setItem('access_token', '${token}');
</script>
<body>
<p>Please follow this link.</p>
</body>
</html>
The configuration wiring in a WebSecurityConfigurerAdapter:
#Configuration
#EnableWebSecurity
#EnableWebMvc
#Import(WebServiceConfig.class)
public class AuthenticationConfig extends WebSecurityConfigurerAdapter {
#Value("${registrationUrl}")
private String registrationUrl;
#Autowired
private SocialAuthenticationSuccessHandler socialAuthenticationSuccessHandler;
#Value("${loginUrl}")
private String loginUrl;
#Override
protected void configure(HttpSecurity http) throws Exception {
List<String> permitAllUrls = new ArrayList<>();
// permit social log in
permitAllUrls.add("/auth/**");
http.authorizeRequests().antMatchers(permitAllUrls.toArray(new String[0])).permitAll();
SpringSocialConfigurer springSocialConfigurer = new SpringSocialConfigurer();
springSocialConfigurer.signupUrl(registrationUrl);
springSocialConfigurer.postFailureUrl(loginUrl);
springSocialConfigurer
.addObjectPostProcessor(new ObjectPostProcessor<SocialAuthenticationFilter>() {
#SuppressWarnings("unchecked")
public SocialAuthenticationFilter postProcess(SocialAuthenticationFilter filter){
filter.setAuthenticationSuccessHandler(socialAuthenticationSuccessHandler);
return filter;
}
});
http.apply(springSocialConfigurer);
http.logout().disable().csrf().disable();
http.requiresChannel().anyRequest().requiresSecure();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
I implemented spring oauth2 to secure my rest services and additionally add social login and implicit signup for first time login . for user user you can generate the token using username and password only problem with generate the token for social user . for that you have to implement the Filter that will intercept your /oauth/token request before processing . here if you want to generate the the token for social user pass the username and facebook token , here you can use facebook token as password and generate the token for facebook user also . if facebook token updated then you have to write a db trigger also to update you token in user table .... may be it will help you

Resources