Web application deployed not generating authorization tokens - java

I'm trying send using gmail api with oauth2 credentials like below
private Credential getCredentials(NetHttpTransport httpTransport) throws IOException {
// Load client secrets.
try {
Resource file = resourceLoader.getResource("classpath:credentials.json");
InputStream inputStream = file.getInputStream();
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY,
new InputStreamReader(inputStream));
// Build flow and trigger user authorization request.
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(httpTransport, JSON_FACTORY,
clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
.setAccessType("offline").build();
return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
} catch (Exception exception) {
exception.printStackTrace();
LOGGER.info("Exception occured:: {}", exception.getMessage());
throw new RecordNotFoundException(exception.getMessage());
}
}
using desktop app's credentials.json file.
When I have deployed in dev server I am unable to generate access and refresh token saved file.
Could you please help me.

AuthorizationCodeInstalledApp OAuth 2.0 authorization code flow for an installed Java application that persists end-user credentials.
You cant deploy that to a server its going to open the authorization window on the server.
For a web application it looks like you should be following this Oauth
public class CalendarServletSample extends AbstractAuthorizationCodeServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
// do stuff
}
#Override
protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
GenericUrl url = new GenericUrl(req.getRequestURL().toString());
url.setRawPath("/oauth2callback");
return url.build();
}
#Override
protected AuthorizationCodeFlow initializeFlow() throws IOException {
return new GoogleAuthorizationCodeFlow.Builder(
new NetHttpTransport(), JacksonFactory.getDefaultInstance(),
"[[ENTER YOUR CLIENT ID]]", "[[ENTER YOUR CLIENT SECRET]]",
Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory(
DATA_STORE_FACTORY).setAccessType("offline").build();
}
#Override
protected String getUserId(HttpServletRequest req) throws ServletException, IOException {
// return user ID
}
}
public class CalendarServletCallbackSample extends AbstractAuthorizationCodeCallbackServlet {
#Override
protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential)
throws ServletException, IOException {
resp.sendRedirect("/");
}
#Override
protected void onError(
HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse)
throws ServletException, IOException {
// handle error
}
#Override
protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
GenericUrl url = new GenericUrl(req.getRequestURL().toString());
url.setRawPath("/oauth2callback");
return url.build();
}
#Override
protected AuthorizationCodeFlow initializeFlow() throws IOException {
return new GoogleAuthorizationCodeFlow.Builder(
new NetHttpTransport(), JacksonFactory.getDefaultInstance()
"[[ENTER YOUR CLIENT ID]]", "[[ENTER YOUR CLIENT SECRET]]",
Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory(
DATA_STORE_FACTORY).setAccessType("offline").build();
}
#Override
protected String getUserId(HttpServletRequest req) throws ServletException, IOException {
// return user ID
}
}

From what I have understood from your question and comments you want to achieve the following:
With your current code send email messages from your application which already works on your local machine and now you want to do it too in a web server.
Considering that you are the only user that needs to authorise the application you can achieve what you are aiming using service accounts.
A service account works best for applications like yours where you don't need authorization from your users but rather from your account to for example send emails automatically.
These type of accounts belong to your application rather than to an individual user and can make API requests without the need to authorise through the UI. This guide from the documentation will walk you step through step on how to set up your authorization with service accounts.
Moreover, bare in mind that in order for the service account to execute requests in your name (as you need an user to send emails) you should use domain-wide authorization so that the service account can make such requests in the name of the user. Also note that you need a Google Workspace account to implement domain wide authorization (let me know if you don't have it as I can propose a workaround).

Related

Java - Token flow OAuth 2 E2E with code

I'm New to security & JAVA and I need to implement token follow of OAuth2, this is the exact flow which I need to implement (if there is some library which can help it's great )
http://tutorials.jenkov.com/oauth2/authorization-code-request-response.html
How can I achieve it with JAVA, I want to use some library that provide this functionality. the token flow should be against the UAA but any other similar example will be very helpful.
i've found this example but not sure how to use/test it E2E with UAA
Postman will be very helpful to simulate it...
https://developers.google.com/api-client-library/java/google-oauth-java-client/oauth2
UAA context
https://github.com/cloudfoundry/uaa
I would suggest you Spring as the most popular framework for building web apps in Java. It has Spring Security module that can facilitate developing OAuth 2.0 clients as well as resource servers, as shown here or here.
For a detailed explanation of the OAuth 2.0 flow, visit RFC 6749 Specification. Regarding a step by step solution, you ought to see some tutorials such as this article explaining how to create a Spring REST API using OAuth 2.0. This article goes through code as well as creating Postman requests. With regards to mocking/tests, I've previously created a test suite for the OAuth 2.0 using TestNG and Mockito.
The more you develop and research, the more you shall find ways of improving or rather change the way you design your code. That said if you really want to abide by the OAuth 2.0 flow, you should properly understand the flow (which can be relatively vague at times) in the RFC 6749 link.
Here is the Google API clinet library sample. Try this if it helps
public class ServletSample extends AbstractAuthorizationCodeServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
// do stuff
}
#Override
protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
GenericUrl url = new GenericUrl(req.getRequestURL().toString());
url.setRawPath("/oauth2callback");
return url.build();
}
#Override
protected AuthorizationCodeFlow initializeFlow() throws IOException {
return new AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(),
new NetHttpTransport(),
new JacksonFactory(),
new GenericUrl("https://server.example.com/token"),
new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"),
"s6BhdRkqt3",
"https://server.example.com/authorize").setCredentialDataStore(
StoredCredential.getDefaultDataStore(
new FileDataStoreFactory(new File("datastoredir"))))
.build();
}
#Override
protected String getUserId(HttpServletRequest req) throws ServletException, IOException {
// return user ID
}
}
public class ServletCallbackSample extends AbstractAuthorizationCodeCallbackServlet {
#Override
protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential)
throws ServletException, IOException {
resp.sendRedirect("/");
}
#Override
protected void onError(
HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse)
throws ServletException, IOException {
// handle error
}
#Override
protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
GenericUrl url = new GenericUrl(req.getRequestURL().toString());
url.setRawPath("/oauth2callback");
return url.build();
}
#Override
protected AuthorizationCodeFlow initializeFlow() throws IOException {
return new AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(),
new NetHttpTransport(),
new JacksonFactory(),
new GenericUrl("https://server.example.com/token"),
new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"),
"s6BhdRkqt3",
"https://server.example.com/authorize").setCredentialDataStore(
StoredCredential.getDefaultDataStore(
new FileDataStoreFactory(new File("datastoredir"))))
.build();
}
#Override
protected String getUserId(HttpServletRequest req) throws ServletException, IOException {
// return user ID
}
}
https://github.com/spring-projects/spring-security-oauth/tree/master/samples/oauth2 contains sample code for performing oauth2 using Spring Security.

Facebook's webhook Application creation failing due to "unable to verify provided URL"

Can't create Webhook applicaion on Facebook because of error "unable to verify provided URL"
The url given for callback is working fine from browser with https port 443
The Servlet code to recieve the post call from webhook as below
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("**************Entering Callback Servlet**************************");
Map<String, String[]> parametersMap = request.getParameterMap();
if (parametersMap.size() > 0) {
if (request.getParameter("hub.mode").equals("streamInit")) {
System.out.println("Verify Token: " + request.getParameter("hub.verify_token"));
System.out.println("Challenge number:" + request.getParameter("hub.challenge"));
String responseToClient = request.getParameter("hub.challenge");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write(responseToClient);
response.getWriter().flush();
response.getWriter().close();
response.getWriter().append("Fetch-Mode").append(request.getParameter("hub.mode"));
response.getWriter().append("App Verify Token:").append(request.getParameter("hub.verify_token"));
response.getWriter().append("App Challenge No").append(request.getParameter("hub.challenge"));
System.out.println("**************Callback Successful**************************");
}
//response.getWriter().append("Served at: ").append(request.getContextPath());
}else{
System.out.println("**************Not an Facebook POST**************************");
}
System.out.println("**************Exiting Callback Servlet**************************");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
Need help on what I'm missing here.
Facebook wants to be pointed to the exact file (eg.: callback.php) you could workaround this with mod rewrite. Just to mention it, the https:// certificate must be valid and signed by a public authority (e.g.: comodo).
You can use https://ngrok.com/ to send https Webhooks to your localhost dev server.
if (request.getParameter("hub.mode").equals("streamInit"))
to
if (request.getParameter("hub.mode").equals("subscribe"))
I did not need to point it at an exact file.

Facebook4j - This authorization code has been used

I am using Facebook4j to post something on facebook. The following code worked the first time, but now it says: "This authorization code has been used". I would assume, this would give me a new access code everytime I use this, but apperently it does not. How can I tell facebook to give me a new access token instead of the same one over and over?
public class FacebookPublisher extends HttpServlet{
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Facebook facebook = new FacebookFactory().getInstance();
facebook.setOAuthAppId("appId", "appSecret");
facebook.setOAuthPermissions("email,manage_pages,publish_pages,publish_actions");
request.getSession().setAttribute("facebook", facebook);
StringBuffer callbackURL = request.getRequestURL();
int index = callbackURL.lastIndexOf("/");
callbackURL.replace(index, callbackURL.length(), "").append("/fb1callback");
response.sendRedirect(facebook.getOAuthAuthorizationURL(callbackURL.toString()));
}
}
This is the callback servlet and code:
public class FacebookPublisherCallback extends HttpServlet {
private static final long serialVersionUID = 6305643034487441839L;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Facebook facebook = (Facebook) request.getSession().getAttribute("facebook");
String oauthCode = request.getParameter("code");
try {
token = facebook.getOAuthAccessToken(oauthCode).getToken();
facebook = new FacebookFactory().getInstance();
facebook.setOAuthAppId("appId", "appSecret");
facebook.setOAuthPermissions("email,manage_pages,publish_pages,publish_actions");
facebook.setOAuthAccessToken(new AccessToken(token));
facebook.postStatusMessage("Hello World from Facebook4J.");
} catch (FacebookException e) {
e.printStackTrace();
}
}
}

OAuth "401: Invalid Credentials" after some period of time

I have created simple app using Google Drive API with OAuth2 authentication based on this sample plus-appengine-sample
So, I have two servlets implementations: AbstractAppEngineAuthorizationCodeServlet and AbstractAppEngineAuthorizationCodeCallbackServlet which should do all the hard work for me (oauth work flow).
public class DriveServlet extends AbstractAppEngineAuthorizationCodeServlet {
private static final String MY_APP_NAME = "Drive API demo";
private static final long serialVersionUID = 1L;
#Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
AuthorizationCodeFlow authFlow = initializeFlow();
Credential credential = authFlow.loadCredential(getUserId(req));
if (credential == null) {
resp.sendRedirect(authFlow.newAuthorizationUrl()
.setRedirectUri(OAuthUtils.getRedirectUri(req)).build());
return;
}
Drive drive = new Drive.Builder(OAuthUtils.HTTP_TRANSPORT_REQUEST,
OAuthUtils.JSON_FACTORY, credential).setApplicationName(MY_APP_NAME).build();
// API calls (examines drive structure)
DriveMiner miner = new DriveMiner(drive);
req.setAttribute("miner", miner);
RequestDispatcher view = req.getRequestDispatcher("/Drive.jsp");
view.forward(req, resp);
}
#Override
protected AuthorizationCodeFlow initializeFlow() throws ServletException, IOException {
return OAuthUtils.initializeFlow();
}
#Override
protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
return OAuthUtils.getRedirectUri(req);
}
}
public class OAuthCallbackServlet extends AbstractAppEngineAuthorizationCodeCallbackServlet {
private static final long serialVersionUID = 1L;
#Override
protected AuthorizationCodeFlow initializeFlow() throws ServletException, IOException {
return OAuthUtils.initializeFlow();
}
#Override
protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
return OAuthUtils.getRedirectUri(req);
}
#Override
protected void onSuccess(HttpServletRequest req, HttpServletResponse resp,
Credential credential) throws ServletException, IOException {
resp.sendRedirect(OAuthUtils.MAIN_SERVLET_PATH);
}
#Override
protected void onError(HttpServletRequest req, HttpServletResponse resp,
AuthorizationCodeResponseUrl errorResponse) throws ServletException, IOException {
String nickname = UserServiceFactory.getUserService().getCurrentUser().getNickname();
resp.getWriter().print(
"<h3>I am sorry" + nickname+ ", an internal server error occured. Try it later.</h1>");
resp.setStatus(500);
resp.addHeader("Content-Type", "text/html");
return;
}
}
public class OAuthUtils {
private static final String CLIENT_SECRETS_FILE_PATH = "/client_secrets.json";
static final JacksonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
static final UrlFetchTransport HTTP_TRANSPORT_REQUEST = new UrlFetchTransport();
private static final Set<String> PERMISSION_SCOPES = Collections.singleton(DriveScopes.DRIVE_READONLY);
private static final AppEngineDataStoreFactory DATA_STORE_FACTORY = AppEngineDataStoreFactory.getDefaultInstance();
private static final String AUTH_CALLBACK_SERVLET_PATH = "/oauth2callback";
static final String MAIN_SERVLET_PATH = "/drive";
private static GoogleClientSecrets clientSecrets = null;
private OAuthUtils() {}
private static GoogleClientSecrets getClientSecrets() throws IOException {
if (clientSecrets == null) {
InputStream jsonStream = OAuthUtils.class.getResourceAsStream(CLIENT_SECRETS_FILE_PATH);
InputStreamReader jsonReader = new InputStreamReader(jsonStream);
clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, jsonReader);
}
return clientSecrets;
}
static GoogleAuthorizationCodeFlow initializeFlow() throws IOException {
return new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT_REQUEST,
JSON_FACTORY, getClientSecrets(), PERMISSION_SCOPES)
.setDataStoreFactory(DATA_STORE_FACTORY)
.setAccessType("offline").build();
}
static String getRedirectUri(HttpServletRequest req) {
GenericUrl requestUrl = new GenericUrl(req.getRequestURL().toString());
requestUrl.setRawPath(AUTH_CALLBACK_SERVLET_PATH);
return requestUrl.build();
}
}
Authentication flow works as expected as well as Drive API calls, but somehow, after some period of time, I'm getting this exception on refresh:
Uncaught exception from servlet
com.google.api.client.googleapis.json.GoogleJsonResponseException: 401
{
"code" : 401,
"errors" : [{ "domain" : "global",
"location" : "Authorization",
"locationType" : "header",
"message" : "Invalid Credentials",
"reason" : "authError" }],
"message" : "Invalid Credentials"
}
at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:145)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:312)
at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1049)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:410)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:343)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:460)
at sk.ennova.teamscom.drive.DriveMiner.getRootFolderId(DriveMiner.java:46)
at org.apache.jsp.Drive_jsp._jspService(Drive_jsp.java:61)
It seems that token has expired, but isn't it a work for servlets to request a new access token with the refresh token which they stored? I use offline access type, so refresh token should be delivered to callback servlet at first request.
Here "401 Unauthorized" when trying to watch changes on Google Drive with Java API Client are some hints where could be the problem, but handling token expiration should not be my case if I'm using these servlets (correct me if I am wrong). Also scope DriveScopes.DRIVE_READONLY seems OK for reading "drive" tree structure (get files of given folder and so on). Where could be the problem?
You need to first specify that you need a refresh token for offline / long term access and then save the refresh token for later use when the access token expires. You can request a new access token using the refresh token until the user revokes your access to her account. See the official documentation here:
https://developers.google.com/accounts/docs/OAuth2WebServer#refresh

how to instantiate verifier when using scribe for facebook authentication

I am testing scribe for facebook authentication. I am not receiving the oauth_verifier when authenticating against facebook - let me know if this is incorrect behavior. For facebook auth, how should I go about creating the verifier in order to create the OAuthRequest.
redirect_uri=http%3A%2F%2Flocalhost%2Foauth%2Ffacebook
Thanks
LoginServlet:
public void doGet(HttpServletRequest req, HttpServletResponse res) {
OAuthService service = new ServiceBuilder().provider(FacebookApi.class).apiKey(FACEBBOK_APP_KEY)
.apiSecret(FACEBOOK_APP_SECRET).callback(FACEBOOK_CALLBACK);
String authenticationUrl = service.getAuthorizationUrl(null);
res.sendRedirect(authenticationUrl);
}
CallbackServlet:
public void doGet(HttpServletRequest req, HttpServletResponse res) {
String code = "";
Enumeration paramEnum = req.getParameterNames();
while (paramEnum.hasMoreElements()) {
String name = (String) paramEnum.nextElement();
if (name.equals("code")) {
code = req.getParameter(name);
}
OAuthService service = new ServiceBuilder().provider(FacebookApi.class).apiKey(FACEBBOK_APP_KEY)
.apiSecret(FACEBOOK_APP_SECRET).callback(FACEBOOK_CALLBACK);
Verifier verifier = new Verifier(code);
//....
}

Categories

Resources