I'm trying to implement "Twitter" login for my web application. I use scribe to simplify things a bit.
My implementation relies of GWT RPC mechanism to get the Authorization url back to the client so the client can call a popup window to redirect to the Autorization Url.
However, when the URL is opened to the new tab and user log in with Twitter account, the page provides the PIN number (from this site: https://api.twitter.com/oauth/authorize) that needs to be typed back into the org.scribe.model.Modifier
This kind of approach will be cumbersome to users. What is needed is that when the user typed in the Twitter username/password that should be it. Or at least automate all the other process.
Am I missing something?
Here's my code:
twitterLogin.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
TwitterService.Util.getInstance().getAuthorizationUrl(new AsyncCallback<String>() {
#Override
public void onSuccess(String result) {
if (result != null)
Window.open(result, "__blank", null);
}
#Override
public void onFailure(Throwable caught) {
}
});
}
});
To authenticate with OAuth, you need to send out 2 requests to the authenticating server:
- First to get the "Request Token"
- Then to get the "Access Token"
Twitter does open the authentication page in a new window where they can type their Twitter username/password, so that's to be expected.
if (req.getRequestURI().equals("/twitter")) {
Token requestToken = service.getRequestToken();
System.out.println("Got the Request Token!" + requestToken.getToken());
session = request.getSession(true);
session.setAttribute("TOKEN", requestToken);
response.sendRedirect(service.getAuthorizationUrl(requestToken));
} else if (req.getRequestURI().equals("/twitter/callback")) {
String code = request.getParameter("oauth_verifier");
System.out.println("Verifier :: " + code);
System.out.println("service.getRequestToken()" + service.getRequestToken());
session = request.getSession(false);
Token requestToken = (Token) session.getAttribute("TOKEN");
System.out.println("requestToken from Session " + service.getRequestToken().getToken() + " Secr" + service.getRequestToken().getSecret());
if (code != null && !code.isEmpty()) {
Verifier verifier = new Verifier(code);
Token accessToken = service.getAccessToken(requestToken, verifier);
OAuthRequest req = new OAuthRequest(Verb.GET, OAUTH_PROTECTED_URL);
service.signRequest(accessToken, req);
Response res = req.send();
response.setContentType("text/plain");
response.getWriter().println(res.getBody());
}
}
Related
Currently when a user logs in to my web server using a web POST form, a custom authenticator and a custom user. I have the CustomUser put into the Session provided by the RoutingContext because, when using RoutingContext#setUser it only changes the user for that request and as soon as the user is redirected from the login processing page to their destination the CustomUser has been lost.
However, it also seems as though the Session in RoutingContext for the new page doesn't have any user stored in the entry where the auth placed the CustomUser, could this be sending a completely different Session?
Routing:
//ROUTE DEFINITIONS
// SESSION AND COOKIE
router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)).setNagHttps(false)); //TODO SSL
router.route().handler(CookieHandler.create());
// STATIC
router.route("/").handler(new StaticHandler()); //BASE
router.route("/admin").handler(new StaticHandler()); //ADMIN PAGE
// FORM REQUESTS
router.route("/login").handler(new AuthAndRegHandler(new CustomAuth(), dbController)); //LOGIN REQUEST
router.route("/logout").handler(new AuthAndRegHandler(new CustomAuth(), dbController)); //LOGOUT REQUEST
// AJAX
router.route("/ajax/updateInvoice").handler(new AjaxHandler());
// ERRORS
router.route().failureHandler(new ErrorHandler());
router.route().handler(handle -> {
handle.fail(404);
});
//END DEFINITIONS
AuthAndRegHandler:
public class AuthAndRegHandler extends AuthHandlerImpl {
private DatabaseController db;
private CustomAuth authProvider;
public AuthAndRegHandler(CustomAuth authProvider, DatabaseController db) {
super(authProvider);
this.db = db;
this.authProvider = authProvider;
}
#Override
public void handle(RoutingContext event) {
Logger log = LoggerFactory.getLogger(this.getClass());
HttpServerResponse response = event.response();
HttpServerRequest request = event.request();
Session session = event.session();
String requestedPath = request.path();
authProvider.setJdbc(db.getJdbc()); //returns a JDBCClient
if(requestedPath.equalsIgnoreCase("/login")) {
if(request.method() != HttpMethod.POST)
event.fail(500);
else {
request.setExpectMultipart(true);
request.endHandler(handle -> {
MultiMap formAtts = request.formAttributes();
String email = formAtts.get("email");
String pw = formAtts.get("password");
log.info(email + ":" + pw + " login attempt");
authProvider.authenticate(new JsonObject()
.put("username", email)
.put("password", pw), res -> {
if(res.succeeded()) {
CustomUser userToSet = (CustomUser) res.result();
session.put("user", userToSet);
log.info("Login successful for " + email);
response.putHeader("Location", "/").setStatusCode(302).end();
} else {
event.fail(500);
log.error("Auth error for " + request.host());
}
});
});
}
}
}
}
CustomAuth returns true every time for testing purposes.
StaticHandler
CustomUser user = session.get("user");
event.setUser(user);
response.putHeader("Content-Type", "text/html");
if(user != null) {
log.info(user.principal().getString("email") + " user detected");
event.setUser(user);
} else
log.info("Null user request detected"); //Constantly outputs, even after a login form has been submitted
I'm not entirely sure what's going wrong here. Vertx has sub-optimal documentation for a rookie like myself on session and handling things without their out-of-the-box implementations. Any help on how to log someone in and maintain their session like a normal website would be appreciated.
For those who stumble upon the same problem, but usually skip the comments:
Vert.x SessionHandler depends on CookieHandler, and the order is important here.
From the Vert.x examples:
router.route().handler(CookieHandler.create());
router.route().handler(sessionHandler);
We're using stormpath with Java & also trying to combine form Login with REST API authentication on the same application.
I've setup stormpath servlet plugin as described here https://docs.stormpath.com/java/servlet-plugin/quickstart.html... This works very fine.
Now, on the same application, we have APIs where I've implemented oAuth authentication with stormpath see here http://docs.stormpath.com/guides/api-key-management/
The first request for an access-token works fine by sending Basic Base64(keyId:keySecret) in the request header and grant_type = client_credentials in the body. Access tokens are being returned nicely. However trying to authenticate subsequent requests with the header Bearer <the-obtained-access-token> does not even hit the application before
returning the following json error message...
{
"error": "invalid_client",
"error_description": "access_token is invalid."
}
This is confusing because I've set breakpoints all over the application and I'm pretty sure that the API request doesn't hit the anywhere within the application before stormpath kicks in and returns this error. And even if stormpath somehow intercepts the request before getting to the REST interface, this message doesn't make any sense to me because i'm certainly making the subsequent API calls with a valid access-token obtained from the first call to get access-token.
I have run out of ideas why this could be happening but i'm suspecting that it may have something to do with stormpath config especially with a combination
of form Login/Authentication for web views and oAuth Athentication for REST endpoints. With that said, here's what my stormpath.properties looks like. Hope this could help point at anything I may be doing wrong.
stormpath.application.href=https://api.stormpath.com/v1/applications/[app-id]
stormpath.web.filters.authr=com.app.security.AuthorizationFilter
stormpath.web.request.event.listener = com.app.security.AuthenticationListener
stormpath.web.uris./resources/**=anon
stormpath.web.uris./assets/**=anon
stormpath.web.uris./v1.0/**=anon
stormpath.web.uris./** = authc,authr
stormpath.web.uris./**/**=authc,authr
Help with this would be highly appreciated.
The problem might be related to an incorrect request.
Is it possible for you to try this code in your app?:
private boolean verify(String accessToken) throws OauthAuthenticationException {
HttpRequest request = createRequestForOauth2AuthenticatedOperation(accessToken);
AccessTokenResult result = Applications.oauthRequestAuthenticator(application)
.authenticate(request);
System.out.println(result.getAccount().getEmail() + " was successfully verified, you can allow your protect operation to continue");
return true;
}
private HttpRequest createRequestForOauth2AuthenticatedOperation(String token) {
try {
Map<String, String[]> headers = new LinkedHashMap<String, String[]>();
headers.put("Accept", new String[]{"application/json"});
headers.put("Authorization", new String[]{"Bearer " + token});
HttpRequest request = HttpRequests.method(HttpMethod.GET)
.headers(headers)
.build();
return request;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
I've prepared an example that demonstrates oauth token creation as well as authorized access to protected pages using access tokens.
It builds off of the servlet example in the Stormpath SDK. The repo can be found here: https://github.com/stormpath/stormpath-java-oauth-servlet-sample
It demonstrates running a servlet application and having an out-of-band program get and use oauth tokens to access protected resources.
The core of the oauth part is in TokenAuthTest.java:
public class TokenAuthTest {
public static void main(String[] args) throws Exception {
String command = System.getProperty("command");
if (command == null || !("getToken".equals(command) || "getPage".equals(command))) {
System.err.println("Must supply a command:");
System.err.println("\t-Dcommand=getToken OR");
System.err.println("\t-Dcommand=getPage OR");
System.exit(1);
}
if ("getToken".equals(command)) {
getToken();
} else {
getPage();
}
}
private static final String APP_URL = "http://localhost:8080";
private static final String OAUTH_URI = "/oauth/token";
private static final String PROTECTED_URI = "/dashboard";
private static void getToken() throws Exception {
String username = System.getProperty("username");
String password = System.getProperty("password");
if (username == null || password == null) {
System.err.println("Must supply -Dusername=<username> -Dpassword=<password> on the command line");
System.exit(1);
}
PostMethod method = new PostMethod(APP_URL + OAUTH_URI);
method.setRequestHeader("Origin", APP_URL);
method.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
method.addParameter("grant_type", "password");
method.addParameter("username", username);
method.addParameter("password", password);
HttpClient client = new HttpClient();
client.executeMethod(method);
BufferedReader br = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream()));
String readLine;
while(((readLine = br.readLine()) != null)) {
System.out.println(readLine);
}
}
private static void getPage() throws Exception {
String token = System.getProperty("token");
if (token == null) {
System.err.println("Must supply -Dtoken=<access token> on the command line");
System.exit(1);
}
GetMethod method = new GetMethod(APP_URL + PROTECTED_URI);
HttpClient client = new HttpClient();
System.out.println("Attempting to retrieve " + PROTECTED_URI + " without token...");
int returnCode = client.executeMethod(method);
System.out.println("return code: " + returnCode);
System.out.println();
System.out.println("Attempting to retrieve " + PROTECTED_URI + " with token...");
method.addRequestHeader("Authorization", "Bearer " + token);
returnCode = client.executeMethod(method);
System.out.println("return code: " + returnCode);
}
}
I am working on a Java desktop application and after some search I was able to authenticate the user using RestTemplate. Now the situation is I have the cookie String at the desktop side(code given below). Now what I would like to do is to do two important things, get which user logged in using that cookie and access(GET,POST,DELETE) secured resources which are marked with #Secured or #PreAuthorize annotation.
here is my authentication code :
#Override
public void initialize(URL location, ResourceBundle resources) {
submitButton.setOnAction(event -> {
if(!(usernameField.getText().isEmpty() && passwordField.getText().isEmpty())){
try {
RestTemplate rest = new RestTemplate();
String jsessionid = rest.execute("http://localhost:8080/j_spring_security_check", HttpMethod.POST,
new RequestCallback() {
#Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
request.getBody().write(("j_username=" + usernameField.getText() + "&j_password=" + passwordField.getText()).getBytes());
}
}, new ResponseExtractor<String>() {
#Override
public String extractData(ClientHttpResponse response) throws IOException {
List<String> cookies = response.getHeaders().get("Cookie");
// assuming only one cookie with jsessionid as the only value
if (cookies == null) {
cookies = response.getHeaders().get("Set-Cookie");
}
String cookie = cookies.get(cookies.size() - 1);
System.out.println("Cookie is "+cookie);
int start = cookie.indexOf('=');
int end = cookie.indexOf(';');
return cookie.substring(start + 1, end);
}
});
// rest.put("http://localhost:8080/rest/program.json;jsessionid=" + jsessionid, new DAO("REST Test").asJSON());
} catch (AuthenticationException e) {
System.out.println("AuthenticationException");
}
} else {
System.out.println("Fields are empty");
}
});
}
Output of program is :
DEBUG: org.springframework.web.client.RestTemplate - Created POST request for "http://localhost:8080/j_spring_security_check"
DEBUG: org.springframework.web.client.RestTemplate - POST request for "http://localhost:8080/j_spring_security_check" resulted in 302 (Found)
Cookie is JSESSIONID=903B2924CCC84421931D52A4F0AA3C7E; Path=/; HttpOnly
If I was on server-side, I would have simply called the below method to get the currently authenticated user :
#Override
public Person getCurrentlyAuthenticatedUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return null;
} else {
return personDAO.findPersonByUsername(authentication.getName());
}
}
How can I get the currently authenticate user on desktop based java app so I can just use below method and authenticate on desktop java app. :
Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
Authentication authentication = new UsernamePasswordAuthenticationToken(person1, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
This way, I can use #Secured annotations for the desktop java app as well. Thanks a lot.
Update
So on the server side I have created a method which gives me the logged in user. As suggested in an answer, I can use the same rest template, but I would like to store the cookie in users local db instead of passing the Resttemplates object around when user clicks here and there.
Server side method :
#Secured("ROLE_USER")
#RequestMapping(value = "/rest/getloggedinuser", method = RequestMethod.GET)
public
#ResponseBody
ResponseEntity<RestPerson> getLoggedInRestUser() {
Person person = this.personService.getCurrentlyAuthenticatedUser();
RestPerson restPerson = new RestPerson();
restPerson.setFirstname(person.getFirstName());
restPerson.setUsername(person.getUsername());
restPerson.setPassword("PROTECTED");
return new ResponseEntity<RestPerson>(restPerson, HttpStatus.OK);
}
Now, next for now, I am trying to use the same RestTemplate to check if this method works with code below, but I would really like to know how I can do this with just a cookie :
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Cookie", cookie);
HttpEntity requestEntity = new HttpEntity(null, requestHeaders);
ResponseEntity rssResponse = rest.exchange(
"/rest/getloggedinuser",
HttpMethod.GET,
requestEntity,
Person.class);
String rssResponseBody = (String)rssResponse.getBody();
System.out.println("Response body is ");
Is there a way to cast the Object in the ResponseBody to the Person object???
If you want to get some user information which is stored on the server-side, you should create a new service, for example "getUserInformation" on your server, which will provide such information.
You should not extract cookies manually, just reuse the same RestTemplate, it stores cookies internally (specifically in the underlying HttpClient). That's how you can reach secure resources.
UPDATE:
You don't need to pass around the RestTemplate, just make it a singleton and use it everywhere.
And rssResponse.getBody(); should return you a Person object, not String.
I get a Signature invalid problem when I try to get data from Magento in Java. What is wrong with my code:
public class MagentoFacade {
final String MAGENTO_API_KEY = "apikey";
final String MAGENTO_API_SECRET = "apisecret";
final String MAGENTO_REST_API_URL = "urlmagento/api/rest";
public void testMethod() {
OAuthService service = new ServiceBuilder()
.provider(MagentoThreeLeggedOAuth.class)
.apiKey(MAGENTO_API_KEY)
.apiSecret(MAGENTO_API_SECRET)
.debug()
.build();
System.out.println("" + service.getVersion());
// start
Scanner in = new Scanner(System.in);
System.out.println("Magento's OAuth Workflow");
System.out.println();
// Obtain the Request Token
System.out.println("Fetching the Request Token...");
Token requestToken = service.getRequestToken();
System.out.println("Got the Request Token!");
System.out.println();
// Obtain the Authorization URL
System.out.println("Fetching the Authorization URL...");
String authorizationUrl = service.getAuthorizationUrl(requestToken);
System.out.println("Got the Authorization URL!");
System.out.println("Now go and authorize Main here:");
System.out.println(authorizationUrl);
System.out.println("And paste the authorization code here");
System.out.print(">>");
Verifier verifier = new Verifier(in.nextLine());
System.out.println();
System.out.println("Trading the Request Token for an Access Token...");
Token accessToken = service.getAccessToken(requestToken, verifier);
System.out.println("Got the Access Token!");
System.out.println("(if your curious it looks like this: "
+ accessToken + " )");
System.out.println();
OAuthRequest request = new OAuthRequest(Verb.GET, MAGENTO_REST_API_URL+ "/products?limit=2");
service.signRequest(accessToken, request);
Response response = request.send();
System.out.println();
System.out.println(response.getCode());
System.out.println(response.getBody());
System.out.println();
}
public static void main(String[] args) {
MagentoFacade mf = new MagentoFacade();
mf.testMethod();
}
}
public final class MagentoThreeLeggedOAuth extends DefaultApi10a {
private static final String BASE_URL = "urltoMagento/";
#Override
public String getRequestTokenEndpoint() {
return BASE_URL + "oauth/initiate";
}
#Override
public String getAccessTokenEndpoint() {
return BASE_URL + "oauth/token";
}
#Override
public String getAuthorizationUrl(Token requestToken) {
return BASE_URL + "richard/oauth_authorize?oauth_token="
+ requestToken.getToken(); //this implementation is for admin roles only...
}
}
signature is: NnRaB73FqCcFAAVB4evZtGkWE3k=
appended additional OAuth parameters: { oauth_callback -> oob , oauth_signature -> NnRaB73FqCcFAAVB4evZtGkWE3k= , oauth_version -> 1.0 , oauth_nonce -> 753236685 , oauth_signature_method -> HMAC-SHA1 , oauth_consumer_key -> ptrij1xt8tjisjb6kmdqed2v4rpla8av , oauth_timestamp -> 1359710704 }
using Http Header signature
sending request...
response status code: 401
response body: oauth_problem=signature_invalid&debug_sbs=MCe/RB8/GNuqV0qku00ubepc/Sc=
Exception in thread "main" org.scribe.exceptions.OAuthException: Response body is incorrect. Can't extract token and secret from this: 'oauth_problem=signature_invalid&debug_sbs=MCe/RB8/GNuqV0qku00ubepc/Sc='
at org.scribe.extractors.TokenExtractorImpl.extract(TokenExtractorImpl.java:41)
at org.scribe.extractors.TokenExtractorImpl.extract(TokenExtractorImpl.java:27)
at org.scribe.oauth.OAuth10aServiceImpl.getRequestToken(OAuth10aServiceImpl.java:52)
at magento.MagentoFacade.testMethod(MagentoFacade.java:39)
at magento.MagentoFacade.main(MagentoFacade.java:73)
I might have an answer for you, but it may not work in your case.
I struggled hard to find out why I got signature invalid on my local machine.
Turns out that when calculating the signature in Mage_Oauth_Model_Server::_validateSignature(), Magento builds the request URI part with the URL port path trimmed : $this->_request->getHttpHost()
In my case, the local webserver runs on port 81, thus my signature and the Magento one could not match.
By passing the false parameter to the getHttpHost method you can keep prevent port trim.
I know this is very specific, but I lost all my hair figuring out why so I needed to share it. And who knows, maybe this could help.
Cheers
Bouni
I'd just like to add that in Postman I simply added another urlparameter of getHttpHost with the value of false and that worked as well. I fought with this for an entire day. I hope this saves someone else time.
I was wondering if I can develop a Desktop App for LinkedIn using Java. I know it can be done as a web application easily, but a completely desktop application, is it possible?
I had a look at the linkedin api's and Java Wrapper for LinkedIn.
The code was explained for a web application. How do I manage that in a java desktop app, specifically the authorization part?
oAuth using Swing?
Please direct me in the right way.
After a very long time of testing with oAuth (with my own wrappers), I settled for Scribe which is a Java Wrapper for almost all oAuth mechanisms. To include Linkedin in a Desktop client, as Adam Trachtenberg (Thank you again) suggested, oob option was used, i.e., after logging in, a code generated by linkedin has to be entered in our Client so that it can be validated against the requested url. Hope this is useful for someone.
public class LinkedInExample
{
private static final String PROTECTED_RESOURCE_URL = "http://api.linkedin.com/v1/people/~/connections:(id,last-name)";
public static void main(String[] args) throws IOException
{
OAuthService service = new ServiceBuilder()
.provider(LinkedInApi.class)
.apiKey("YourApiKey")
.apiSecret("YourApiSecret")
.build();
Scanner in = new Scanner(System.in);
//BareBonesBrowserLaunch.openURL("www.google.com");
System.out.println("=== LinkedIn's OAuth Workflow ===");
System.out.println();
// Obtain the Request Token
System.out.println("Fetching the Request Token...");
Token requestToken = service.getRequestToken();
System.out.println("Got the Request Token!");
System.out.println();
System.out.println("Now go and authorize Scribe here:");
String authURL = service.getAuthorizationUrl(requestToken);
System.out.println(authURL);
BareBonesBrowserLaunch.openURL("www.google.com");
System.out.println("And paste the verifier here");
System.out.print(">>");
Verifier verifier = new Verifier(in.nextLine());
System.out.println();
// Trade the Request Token and Verfier for the Access Token
System.out.println("Trading the Request Token for an Access Token...");
Token accessToken = service.getAccessToken(requestToken, verifier);
System.out.println("Got the Access Token!");
System.out.println("(if your curious it looks like this: " + accessToken + " )");
System.out.println();
// Now let's go and ask for a protected resource!
System.out.println("Now we're going to access a protected resource...");
OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL);
service.signRequest(accessToken, request);
Response response = request.send();
System.out.println("Got it! Lets see what we found...");
System.out.println();
System.out.println(response.getBody());
System.out.println();
System.out.println("Thats it man! Go and build something awesome with Scribe! :)");
}
}
The BareBonesBrowserLaunch is used to launch the default browser with the Linkedin URL for the token request in most OS's. Since the Desktop part is not available in Java 1.5, the BareBonesBrowserLaunch solves the problem.
public class BareBonesBrowserLaunch {
static final String[] browsers = { "google-chrome", "firefox", "opera",
"epiphany", "konqueror", "conkeror", "midori", "kazehakase", "mozilla" };
static final String errMsg = "Error attempting to launch web browser";
public static void openURL(String url) {
try { //attempt to use Desktop library from JDK 1.6+
Class<?> d = Class.forName("java.awt.Desktop");
d.getDeclaredMethod("browse", new Class[] {java.net.URI.class}).invoke(
d.getDeclaredMethod("getDesktop").invoke(null),
new Object[] {java.net.URI.create(url)});
//above code mimicks: java.awt.Desktop.getDesktop().browse()
}
catch (Exception ignore) { //library not available or failed
String osName = System.getProperty("os.name");
try {
if (osName.startsWith("Mac OS")) {
Class.forName("com.apple.eio.FileManager").getDeclaredMethod(
"openURL", new Class[] {String.class}).invoke(null,
new Object[] {url});
}
else if (osName.startsWith("Windows"))
Runtime.getRuntime().exec(
"rundll32 url.dll,FileProtocolHandler " + url);
else { //assume Unix or Linux
String browser = null;
for (String b : browsers)
if (browser == null && Runtime.getRuntime().exec(new String[]
{"which", b}).getInputStream().read() != -1)
Runtime.getRuntime().exec(new String[] {browser = b, url});
if (browser == null)
throw new Exception(Arrays.toString(browsers));
}
}
catch (Exception e) {
JOptionPane.showMessageDialog(null, errMsg + "\n" + e.toString());
}
}
}
}
The LinkedInExample is taken mostly from this library - https://github.com/fernandezpablo85/scribe-java/downloads
Don't forget to include the Scribe jar and apache commons-codec (for Base64)
Yes you can it's all about playing with the API and utilizing the web services packed within the LinkedIn's API.
However, the entire process has to be implemented by using the HTTP requests etc and by parsing the response to render it on the JForm.
EDIT: Ahh! you are totally independent :-) thanks to XML..
If you can't figure out how to redirect the user to a web browser and have the browser redirect back to your application, check out the "out of bounds" (aka "oob") option for the OAuth callback. This will display a code to the member after they authorize your application, which they can type into your Java app.