I have a set of RESTful web services built on DropWizard. Currently I am using BasicAuth to authenticate the users to use the API .
That involves an overhead of having another DB with user/password details. I was looking about token based authentication and found that DropWizard supports Oauth2 out of the box.
Can anyone help me with a sample implementation of this Oauth2 based authentication ? And what would be the architecture to implement so ?
Any help would be appreciated.
This question has been around for a while, but for future visitors I place an article here which explains how to do it with custom annotations very well:
Implement secure API authentication over HTTP with Dropwizard on Automation Rhapsody
Basically the idea is to implement our own annotations with our own logic (which in this case is using JWT), but the post also points out what custom settings are reqired for Dropwizard.
Even though this question is four years old I wasn't able to find a fully working example of an application that plugs into dropwizard Oauth2 library with your own validation mechanism.
So for the benefit of people who stumble upon this post from google search in future, here is a full working example running on latest dropwizard version 1.3.8
Good luck!
There is an example of OAuth2 authentication in Dropwizard GitHub repo.
Below there is an example for latest version of Dropwizard (v0.7.1):
...
public OAuthFactory(final Authenticator<String, T> authenticator,
final String realm,
final Class<T> generatedClass) {
super(authenticator);
this.required = false;
this.realm = realm;
this.generatedClass = generatedClass;
}
private OAuthFactory(final boolean required,
final Authenticator<String, T> authenticator,
final String realm,
final Class<T> generatedClass) {
super(authenticator);
this.required = required;
this.realm = realm;
this.generatedClass = generatedClass;
}
#Override
public AuthFactory<String, T> clone(boolean required) {
return new OAuthFactory<>(required, authenticator(), this.realm, this.generatedClass);
}
public T provide() {
try {
final String header = request.getHeader(HttpHeaders.AUTHORIZATION);
if (header != null) {
final int space = header.indexOf(' ');
if (space > 0) {
final String method = header.substring(0, space);
if (PREFIX.equalsIgnoreCase(method)) {
final String credentials = header.substring(space + 1);
final Optional<T> result = authenticator().authenticate(credentials);
if (result.isPresent()) {
return result.get();
}
}
}
}
} catch (AuthenticationException e) {
LOGGER.warn("Error authenticating credentials", e);
throw new InternalServerErrorException();
}
if (required) {
throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED)
.header(HttpHeaders.WWW_AUTHENTICATE, String.format(CHALLENGE_FORMAT, realm))
.type(MediaType.TEXT_PLAIN_TYPE)
.entity("Credentials are required to access this resource.")
.build());
}
return null;
}
#Override
public Class<T> getGeneratedClass() {
return generatedClass;
}
...
Complete code, here!
Related
I am currently using Google management API for analytics v2.4, but the way i do the authentication is deprecated (function setUserCredentials), so I need to migrate to OAuth2.0 authentication, but I am having some problems to understand google documentation, about it.
DataQuery query = new DataQuery(new URL(DATA_URL));
query.setIds(tableId);
query.setStartDate(startDate);
query.setEndDate(endDate);
query.setDimensions(dimensions);
query.setMetrics(metrics);
query.setMaxResults(10000);
query.setStringCustomParameter("key", "API_key");
return query;
}
private static DataFeed getDataFeed(String username, String password, String startDate, String endDate, String dimensions, String metrics) throws ServiceException, IOException {
AnalyticsService myService = new AnalyticsService("xxxxxx");
myService.setUserCredentials(username, password);
DataQuery query = getQuery(GA_ID, startDate, endDate, dimensions, metrics, 1);
DataFeed data = null;
try{
data = myService.getFeed(query, DataFeed.class);
}catch(Exception e){
e.printStackTrace();
}
return data;
}
Can I still use the v2.4 and do the authentication with OAuth2.0 ?
I'm sorry to say that the v2.4 API is also deprecated. If it isn't already shutdown it might be without warning.
If you already have rewrite your code because of the auth issue, you should upgrade to Analytics Reporting API v4 and Analytics Management API v3. See the quick start
guides.
I'm trying to solve a puzzle with enabling OAuth2-based authentication for my Feign client that is used for cross-service communication.
In normal cases, when a user pushes a request through API, I'm able to take all authentication details needed from the Spring's SecurityContextHolder (as it normally does its job and fills all the details objects) and enhance Feign's Request as follows:
public class FeignAccessTokenRelyInterceptor implements RequestInterceptor {
private static final Logger log = LoggerFactory.getLogger(FeignAccessTokenRelyInterceptor.class);
#Override
public void apply(RequestTemplate template) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
String tokenValue = null;
if (auth.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) auth.getDetails();
tokenValue = details.getTokenValue();
}
if (tokenValue == null) {
log.warn("Current token value is null");
return;
}
template.header("Authorization", "Bearer " + tokenValue);
}
}
}
However, when it comes to scheduled calls that are triggered inside the system, the SecurityContext is obviously empty. I'm filling it with UserInfoTokenServices by manually requesting the access token by client credentials flow beforehand and loading user details:
OAuth2Authentication authentication = userInfoTokenServices.loadAuthentication(accessToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
But such construction doesn't fill OAuth2Authentication.details, on which I rely to get an access token. I tried extending OAuth2AuthenticationDetails, but the only constructor requires HttpServletRequest which is hard to get inside a scheduled task, and making a dummy instance of it feels like a bad choice.
So far, I see the only adequate option to make a separate custom implementation of details holder and pass it to OAuth2Authentication along with the access token I have. And then pick it up in FeignAccessTokenRelyInterceptor.
The question
Maybe there are some other options where I can store my access token in the security context and reliably get it from there, in order not to produce new custom classes?
Will be glad for any help.
Some related links I've studied:
How to get custom user info from OAuth2 authorization server /user endpoint
Spring Boot / Spring Cloud / Spring Security: How to correctly obtain an OAuth2 Access Token in a scheduled task
Spring #FeignClient , OAuth2 and #Scheduled not working
How can I authenticate a system user for scheduled processes in Spring?
For the history, hope that'd help someone like me struggling with that.
I didn't find a better way than the initial one and made a custom InternalOAuth2Details to hold a token value obtained from Spring's OAuth services. Then, in the FeignAccessTokenRelyInterceptor I simply check if current details are InternalOAuth2Details and try to get a token value from it, as follows:
#Override
public void apply(RequestTemplate template) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
String tokenValue = null;
if (auth.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) auth.getDetails();
tokenValue = details.getTokenValue();
} else if (auth.getDetails() instanceof InternalOAuth2Details) {
InternalOAuth2Details details = (InternalOAuth2Details) auth.getDetails();
tokenValue = details.getTokenValue();
}
if (tokenValue == null) {
log.warn("Current token value is null");
return;
}
template.header("Authorization", "Bearer " + tokenValue);
}
}
I bet it isn't the best solution, but it seems to work quite stable as of now.
I want to implement token based authentication for web socket connection.
My backend system is server and a machine is client, how to implement authentication during sending and receiving message between machine and server, no user authentication here.
#Configuration
#EnableWebSocketMessageBroker
public class MyConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(new ChannelInterceptorAdapter() {
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
String authToken = accessor.getFirstNativeHeader("X-Auth-Token");
log.debug("webSocket token is {}", authToken);
Principal user = ... ; // access authentication header(s)
accessor.setUser(user);
}
return message;
}
});
}
}
However, I'm totally lost how I would do at "Principal user = ... ;". How would I get Principle with the token? As here no user exist, only communication between a machine and a backend server
Guess you can try to get principal from spring security context
SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Sounds like you might need a solution similar to something that would be used by someone building an IoT solution. A good starting point would be to have a look at OpenID Connect.
There's a good article here discussing the related challenges and solutions:
https://www.gluu.org/blog/oauth2-for-iot/
OpenId Connect's site:
http://openid.net/connect/
I know this issue has been around for a long time, and there are already plenty of answers available on Stack Overflow, such as this and this. These answers indeed fix this issue but all are trying to have their own implementations of the Principle interface which is not needed as Spring Security has already implemented all we need.
Answer your question: The principle needs to be created from the token provided in the request. You can easily convert string token to a Principle by using new BearerTokenAuthenticationToken("token-string-without-bearer"), then after that, you should authenticate the token by using authenticationManager.authenticate(bearerTokenAuthenticationToken). Therefore, the code is like this:
// access authentication header(s)
Principal user = authenticationManager.authenticate(new BearerTokenAuthenticationToken(authToken));
The full implementation of the configureClientInboundChannel method should be (sorry I only have Kotlin code, but you should be able to get the idea):
override fun configureClientInboundChannel(registration: ChannelRegistration) {
registration.interceptors(object : ChannelInterceptor {
override fun preSend(message: Message<*>, channel: MessageChannel): Message<*>? {
val accessor = MessageHeaderAccessor
.getAccessor(message, StompHeaderAccessor::class.java)
if (accessor != null && accessor.command == StompCommand.CONNECT) {
// assume the value of Authorization header has Bearer at the beginning
val authorization = accessor.getFirstNativeHeader("Authorization")
?.split(" ", limit = 2)
?: return message
// check token type is Bearer
if (authorization.getOrNull(0) == "Bearer") {
val token = authorization.getOrNull(1)?.takeIf { it.isNotBlank() }
// if token exists, authenticate and (if succeeded) then assign user to the session
if (token != null) {
val user = try {
authenticationManager.authenticate(BearerTokenAuthenticationToken(token))
} catch (ex: AuthenticationException) {
// if throw an exception, do not touch the user header
null
}
if (user != null) {
accessor.user = user
}
}
}
}
return message
}
})
}
Note that BearerTokenAuthenticationToken comes from org.springframework.boot:spring-boot-starter-oauth2-resource-server, so you need this dependency in order to use it.
authenticationManager comes from the implementation of WebSecurityConfigurerAdapter, exposing as a Bean by overriding authenticationManagerBean method and mark it as #Bean, and then injecting the Bean into your AbstractWebSocketMessageBrokerConfigurer implementation, which in this case, is MyConfig.
Also, don't forget to mark MyConfig class with #Order(Ordered.HIGHEST_PRECEDENCE + 99)
so that it sets up the user header before Spring Security takeover.
I have this very basic authentication for my app:
MapVerifier mapVerifier = new MapVerifier();
mapVerifier.getLocalSecrets().put("user", "pass".toCharArray());
ChallengeAuthenticator guard= new ChallengeAuthenticator(null, ChallengeScheme.HTTP_BASIC, "Secured Resources");
guard.setContext(getContext());
guard.setVerifier(mapVerifier);
How do I adapt this to use Google authentication scheme? That, instead of showing the Username/Password browser popup, it will go to the Google authentication page.
I think that you aren't in the context of a challenge authentication and you need to leverage the authentication service of Google.
Here is an implementation of this approach (not tested) if you want a custom Restlet Authenticator implementation:
public class GoogleAuthenticator extends Authenticator {
private UserService userService;
public GoogleAuthenticator(Context context) {
super(context);
this.userService = UserServiceFactory.getUserService();
}
protected User createUser(com.google.appengine.api.users.User googleUser,
Request request, Response response) {
return new User(googleUser.getUserId());
}
protected boolean authenticate(Request request, Response response) {
// Get current Google user
com.google.appengine.api.users.User googleUser = userService.getCurrentUser();
// Check if the user is authenticated
if (googleUser!=null) {
// Authenticated through Google authentication service
request.getClientInfo().setUser(
createUser(googleUser, request, response));
return true;
} else {
// Not authenticated. Redirect to the login URL
response.redirectSeeOther(userService.createLoginURL(
request.getRequestURI()));
return false;
}
}
}
However such authenticator exists in the extension org.restlet.ext.gae for a while. It leverages the service UserService of GAE. So I think that you have it with the version of Restlet you use. Here is a sample of use below:
public Restlet createInboundRoot() {
Router router = new Router(getContext());
(...)
GaeAuthenticator guard= new GaeAuthenticator(getContext());
guard.setNext(router);
return guard;
}
Edited:
You can notice that the GAE authenticator can use the GAE enroler for this purpose (i.e. if it's an admin one).
To implement this, you simply need to instantiate such enroler and set it on your authenticator, as desribed below:
GaeEnroler enroler = new GaeEnroler();
GaeAuthenticator guard = new GaeAuthenticator(getContext());
guard.setEnroler(enroler)
guard.setNext(router);
Within your server resource, you can then check the role, as described below:
protected boolean hasAdminRole() {
ClientInfo clientInfo = getClientInfo();
List<Role> roles = clientInfo.getRoles();
boolean isAdmin = false;
for (Role role : roles) {
if (role.getName().equals("admin")) {
isAdmin = true;
break;
}
}
return isAdmin;
}
#Post
public XX handlePost(YY content) {
if (!hasAdminRole()) {
throw new ResourceException(Status.CLIENT_ERROR_FORBIDDEN);
}
(...)
}
Hope it helps you,
Thierry
I haven't fully understood what's ur question is ? If u wanted to integrate Google authentication in yours system check the link
google Oauth2
It's not depend upon any framework it's simply redirection and callback which u can do with plain servlets , obviously you can do with restlets too
I have written an simply library to integrate google and Facebook oauth 2, you can check this to see how it works
java oauth2 gae
Please, at least pseudo (but from working environment not "maybe this should work") application context and controller/filter that will authenticate and/or auto-register Facebook users.
This link: http://blog.kadirpekel.com/2009/11/09/facebook-connect-integration-with-spring-security/ will not do. Actually I will put minus point to anyone who will post it as answer. I spend 2 hours with the thing and I didn't get it to work. I ended bit more bolder and feeling more stupid than usual after this endeavor :-(
I would really like to see OAuth 2.0 solution for facebook connect. And restrict the use of Facebook JavaScript API to absolute minimum.
Following link shows about what I need:
http://www.richardnichols.net/2010/06/implementing-facebook-oauth-2-0-authentication-in-java/
Please post only code to this question. I already got all the advice I can handle.
UPDATE
I have servlet solution and posted answer here if anyone is interested:
Facebook Connect example in JSP (tomcat)
Here's an MVC implementation of facebook OAuth 2.0
The code's in C# and hopefully its similarity with java helps you out.
Controller(Entry point):Controller(in MVC) is the point in the code where the control reaches after someone clicks on the login link.
public ActionResult Authenticate()
{
var oauthFacebook = new FacebookOAuth();
if (Request["code"] == null)
{
//Redirect the user to Facebook for authorization.
Response.Redirect(oauthFacebook.AuthorizationLinkGet());
}
else
{
//Get the access token and secret.
oauthFacebook.AccessTokenGet(Request["code"]);
if (oauthFacebook.Token.Length > 0)
{
//We can now make our api calls
var user = oauthFacebook.GetAttributes();
}
}
}
FacebookOAuth Class
public class FacebookOAuth : Oauth
{
public FacebookOAuth()
{
Authorize = "https://graph.facebook.com/oauth/authorize";
AccessToken = "https://graph.facebook.com/oauth/access_token";
CallbackUrl = "http://<YourURLHere>/Authenticate";
AttributesBaseUrl = "https://graph.facebook.com/me/?access_token=";
ConsumerKey = ConfigurationManager.AppSettings["FacebookConsumerKey"];//Ur Consumer Key goes here
ConsumerSecret = ConfigurationManager.AppSettings["FacebookConsumerSecret"];//Ur Consumer secret goes here
Provider = "Facebook";
}
public override string AuthorizationLinkGet()
{
return
string.Format(
"{0}?client_id={1}&redirect_uri={2}&scope=email,user_education_history,user_location,user_hometown",
Authorize, ConsumerKey, CallbackUrl);
}
public User GetAttributes()
{
string attributesUrl = string.Format("{0}{1}", AttributesBaseUrl, Token);
string attributes = WebRequest(Method.Get, attributesUrl, String.Empty);
var FacebookUser = new JavaScriptSerializer().Deserialize<FacebookUser>(attributes);
return new User()
{
FirstName = FacebookUser.first_name,
MiddleName = FacebookUser.middle_name,
LastName = FacebookUser.last_name,
Locale = FacebookUser.locale,
UserEmail = FacebookUser.email,
AuthProvider = Provider,
AuthToken=Token
};
}
}
OAuth baseclass(Class from which FacebookOAuth derives)
public abstract class Oauth
{
#region Method enum
public enum Method
{
Get,
Post,
Delete
} ;
#endregion
protected string AccessToken;
protected string AttributesBaseUrl;
protected string Authorize;
protected string CallbackUrl;
protected string ConsumerKey;
protected string ConsumerSecret;
public string Provider { get; protected set; }
public string Token { get; set; }
public virtual string AuthorizationLinkGet()
{
return
string.Format(
"{0}?client_id={1}&redirect_uri={2}&scope=publish_stream,email,user_education_history,user_location",
Authorize, ConsumerKey, CallbackUrl);
}
public void AccessTokenGet(string authToken)
{
Token = authToken;
string accessTokenUrl = string.Format("{0}?client_id={1}&redirect_uri={2}&client_secret={3}&code={4}",
AccessToken, ConsumerKey, CallbackUrl, ConsumerSecret, authToken);
string response = WebRequest(Method.Get, accessTokenUrl, String.Empty);
if (response.Length > 0)
{
//Store the returned access_token
NameValueCollection qs = HttpUtility.ParseQueryString(response);
if (qs["access_token"] != null)
{
Token = qs["access_token"];
}
}
}
public string WebRequest(Method method, string url, string postData)
{
StreamWriter requestWriter;
string responseData = string.Empty;
var webRequest = System.Net.WebRequest.Create(url) as HttpWebRequest;
if (webRequest != null)
{
webRequest.Method = method.ToString();
webRequest.ServicePoint.Expect100Continue = false;
webRequest.Timeout = 20000;
if (method == Method.Post)
{
webRequest.ContentType = "application/x-www-form-urlencoded";
//POST the data.
requestWriter = new StreamWriter(webRequest.GetRequestStream());
try
{
requestWriter.Write(postData);
}
finally
{
requestWriter.Close();
}
}
responseData = WebResponseGet(webRequest);
}
return responseData;
}
public string WebResponseGet(HttpWebRequest webRequest)
{
StreamReader responseReader = null;
string responseData;
try
{
responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream());
responseData = responseReader.ReadToEnd();
}
finally
{
if (webRequest != null) webRequest.GetResponse().GetResponseStream().Close();
if (responseReader != null) responseReader.Close();
}
return responseData;
}
}
I actually just finished my non-javascript, implementation of the Facebook Graph API Authentication last night. I was a gargantuan pain in the a**, but it works and it's working fairly well.
I used the example from the link you posted above as a starting point, as well as, the code from here as a starting point. I had to write my own implementation of their FacebookGraphAuthenticationProvider and their FacebookGraphAuthenticationFilter, but now it works the way I want it to.
You need to create implementations of both of these files, put your filter in the filter chain, and create a implementation of the Spring Security UserDetailsService that the Provider can use to manage your user account information. I have some code on my machine at home that I can send you via email if you like.
Here are the steps I had to use to get the authentication to work:
Get an "code" for a user, this is done by making the following call: https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_uri=YOUR_URL&scope=email,read_stream (The scope is all the permissions you want to request from FB). This call will create an "authentication code" which will then be sent back to your "redirect_uri" (which I stated as http://{my fb app registered domain}/j_spring_security_authentication_check.
Once you have this "code", you need to make a call within your AuthenticationProvider that will retrieve an access_token for your user's session: this URL looks like: https://graph.facebook.com/oauth/access_token? client_id=YOUR_APP_ID&redirect_uri=YOUR_URL&client_secret=YOUR_APP_SECRET&code=THE_CODE_FROM_ABOVE. You have to make sure your "redirect_uri" is the same as the one you did in #1. You'll make the above call using something like Apache's HttpClient, or the like.
Now with this access_token (which comes in the body of above response), you can get your user's profile information with the following URL: https://graph.facebook.com/me?access_token={ACCESS_TOKEN from above). The response will be in JSON. You can also use the access_token with all of the graph API to post status, pictures, etc.
I have some code at home that has my full implementation that I would be happy to share.
I hope this helps at least a bit. I suggest using the Spring Social app to get started with posting status, pictures, wall stuff, etc. This will be a good place to start looking at FB-Spring interaction.