Does anyone know the correct setup for the cloud project and redirect URL within the application for the following case?
Setup:
Spring + Apache Wicket
The application is installed on a server (Windows, Linux*) and accessed on the intranet via browser.
*) with or without desktop
Requirements:
Access to one or more Gmail-Accounts to retrieve emails, mark emails as read and move emails to trash
Credentials are stored for each account separately on the server
Creation of the access is done on a client by an admin user in the browser
Consent for an account is done only once on creation, emails are retrieved in a background thread (no user interaction, token is refreshed automatically)
No additional setups on the clients (e.g. changing the host-file, running a background-process/listener); Client could also be a mobile device accessing the intranet
Scopes:
Non-Restricted: userinfo.email
Restricted: gmail.modify
Cloud projects setups/attempts:
Cloud project: Desktop-App; Application: AuthorizationCodeInstalledApp.authorize - Does not work - the consent screen is opened on the server if this is used
Cloud project: Desktop-App; Application: urn:ietf:wg:oauth:2.0:oob as redirect url and popup on the client - Worked but Google is discontinuing oob
Current: Cloud project: Web-App with a public redirect url; Application: redirected to our website - only to show the auth code, which can be pasted in the application open in the browser
public String getAuthorizationUrl(String clientId, String clientSecret, String credentialPath)
{
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
final List<String> SCOPES =
Arrays.asList(new String[] {GmailScopes.GMAIL_MODIFY, Oauth2Scopes.USERINFO_EMAIL});
Details details = new Details();
details.setClientId(clientId);
details.setClientSecret(clientSecret);
GoogleClientSecrets clientSecrets = new GoogleClientSecrets();
clientSecrets.setInstalled(details);
// Build flow and trigger user authorization request.
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(new File(credentialPath)))
.setApprovalPrompt("force")
.setAccessType("offline")
.build();
/* approval prompt and access type were not needed for desktop-app;
* refresh token was generated anyway, they had to be added for web-app
* to get a refresh token */
String redirUri = "https://example.com/redirect";
AuthorizationCodeRequestUrl authorizationUrl =
flow.newAuthorizationUrl().setRedirectUri(redirUri);
return authorizationUrl.build();
}
Google Oauth verification:
Google says that according to the generated traffic, the app is running on a web server and we need to change it to a local URL, otherwise we need a security assessment because the data is stored on a web server.
While it's technically true that it's running on a web server, it's an intranet server.
It's not possible to define a fixed local URL since the servers IP could be different for each user that is installing the app on his server.
You have several issues here. The first is that you are using a desktop application to run a web app. GoogleAuthorizationCodeFlow.Builder is designed for use with installed apps desktop apps or console applications. Its not designed to be run hosted on a web server.
Follow the following example Web server applications
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(), GsonFactory.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(), GsonFactory.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
}
}
installing app.
You have stated this
It's not possible to define a fixed local URL since the servers IP could be different for each user that is installing the app on his server.
Which implies to me that you are giving the code for this app directly to your users with out it being compiled. This includes your credeitnals.json file. YOu may not do this this is against the TOS. Can I really not ship open source with Client ID?
Asking developers to make reasonable efforts to keep their private keys private and not embed them in open source projects.
You should be instructing your users in how to create their own client id and client secrete. in order to get their own creditnals.json file.
They can then supply their own ip address of their server.
In which case your issue with verification is no longer an issue. You dont need to verfy for them. They should be doing that themselves.
push back on internal app
When your users go to verification their app make sure that they are clear with Google that this is an internal app. Hosted on their intranet. They should not need verification.
Related
i tries to add member to my google sheet using java code, but it doesn't work.
I use OAuthor 2.
Can anyone help me with this problem? Thanks very much.
My code:
public static void main(String... args) throws IOException, GeneralSecurityException {
// Build a new authorized API client service.
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
final String spreadsheetId = "10gLncj6bGmm-UcXP1vztsbv23CD85GNG95zjDgZ8HBA";
JsonBatchCallback<Permission> callback = new JsonBatchCallback<Permission>() {
#Override
public void onFailure(GoogleJsonError e,
HttpHeaders responseHeaders)
throws IOException {
// Handle error
System.err.println(e.getMessage());
}
#Override
public void onSuccess(Permission permission,
HttpHeaders responseHeaders)
throws IOException {
System.out.println("Permission ID: " + permission.getId());
}
};
Drive driveService = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))
.setApplicationName(APPLICATION_NAME)
.build();
BatchRequest batch = driveService.batch();
Permission userPermission = new Permission()
.setType("user")
.setRole("reader")
.setEmailAddress("trilo10101990#gmail.com");
driveService.permissions().create(spreadsheetId, userPermission)
.setFields("id")
.queue(batch, callback);
batch.execute();
}
Out put :
Insufficient Permission: Request had insufficient authentication scopes.
Insufficient Permission: Request had insufficient authentication scopes.
Means that the user you have authenticated the application with has not granted your application enough scopes to access the data you are trying to access.
You appear to be using Permissions.create this method requires that the user be authorized with one of the following scopes
You didn't post all of your authorization code You probably have something like this.
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow
.Builder(httpTransport, JSON_FACTORY, clientSecrets,
DriveScopes.all()).setDataStoreFactory(dataStoreFactory)
.build();
The trick is to look for which scope you are requesting in the case of the code above it is requesting full access to google drive. You need to check your scope and ensure that you have requested full access.
After that you need to remove the users consent to your application and force it to request access of the user again. The user needs to see the consent screen in order to verify their consent.
I have a web app where users have to authenticate using Google sign-in. I do this because I need to grab their email address. When they fill out the fields on the page, all that data is stored in a google sheet alongside their email address (for auditing purposes incase something is askew with the data). Unfortunately what's happening is that if user A signs in, and does some work and at the same time user B logs in, when user A submits data, they will be submitting user B's email address (as does user B). In short, the latest person to log in, that email address is used. There is no database and I'm not storing any cookies. When they refresh the page, they have to re-authenticate. I am using Angular 7 and Java. Here is the code that I used:
ngOnInit() {
gapi.load('auth2', () => {
this.auth2 = gapi.auth2.init({
client_id: 'CLIENT_ID_HERE',
// Scopes to request in addition to 'profile' and 'email'
scope: 'https://www.googleapis.com/auth/spreadsheets'
});
});
}
signInWithGoogle(): void {
this.auth2.grantOfflineAccess().then((authResult) => {
this.authCode = authResult['code'];
this.fetchData();
});
}
authCode is bound to the child component so it can be passed as query param to the java code for google auth.
this.seriesService.submitSeriesData(matchList, this.authToken).subscribe(res => {.....);
The google auth java code is so:
private static final String APPLICATION_NAME = "Google Sheets API Java";
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
private static final List<String> SCOPES = Collections.singletonList(SheetsScopes.SPREADSHEETS);
private static final String CLIENT_SECRET_DIR = "/client_secret.json";
private static GoogleTokenResponse tokenResponse = null;
public static String getEmailAddress() throws IOException {
GoogleIdToken idToken = tokenResponse.parseIdToken();
GoogleIdToken.Payload payload = idToken.getPayload();
String email = payload.getEmail();
return email;
}
public static Sheets getSheetsService1(String token, String redirectUri) throws IOException, GeneralSecurityException {
// Exchange auth code for access token
InputStream in = GoogleAuthUtil.class.getResourceAsStream(CLIENT_SECRET_DIR);
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
tokenResponse =
new GoogleAuthorizationCodeTokenRequest(
new NetHttpTransport(),
JacksonFactory.getDefaultInstance(),
"https://www.googleapis.com/oauth2/v4/token",
clientSecrets.getDetails().getClientId(),
clientSecrets.getDetails().getClientSecret(),
token,
redirectUri)
.execute();
String accessToken = tokenResponse.getAccessToken();
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
Sheets service = new Sheets.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance(), credential)
.setApplicationName("MY APP HERE")
.build();
return service;
}
And the endpoint:
#RequestMapping(value="series/data", method = RequestMethod.POST, consumes="application/json")
public boolean submitSeriesMatchData(#RequestBody(required=true) SubmitStatsDto request) throws IOException, GeneralSecurityException, Exception {
if (service == null) {
service = GoogleAuthUtil.getSheetsService1(request.getToken(), this.redirectUri);
}
......
}
1) User clicks on the google sign in button
2) They select email and auth with google
3) I receive an auth code back and store it in ng.
4) Every REST call is passed said token to auth with google, and every endpoint calls getSheetsService1 which authenticates w/ token. (multiple endpoints, I only showed one above)
5) I get email from that tokenResponse.
Any ideas? This site will not have a database/users/local logins. Thank you.
I have a desktop Java which access to my Blogger account. To authenticate with the Google Services, I just use the login and password system, which it is now disabled.
This is a short example on how I do that:
public void subePagina(String pagina) throws MalformedURLException, IOException, ServiceException {
myEntry = new Entry();
myEntry.setTitle(new PlainTextConstruct(title_page));
myEntry.setContent(new HtmlTextConstruct(page));
myEntry.setDraft(false);
activaServicio();
URL editUrl = new URL("url_feed_entry);
myService.update(editUrl, myEntry);
}
public void activaServicio() throws AuthenticationException {
myService = new GoogleService("blogger","blogname");
myService.setUserCredentials(user, password);
}
I've tried to find a solution but I couldn't see enough information to do that, so, my question is, how can I do the same but ysing OAuth? I need to change more code than the activaServicio Method() ?
Thank you.
The title might be a bit misleading and might give you the impression this is an easy one so I will elaborate.
I have a set of endpoints (REST services) that I want to secure without using the regular login way that Spring security provides. Usually you would first aim to the login endpoint (j_pring_security_check by default), authenticate and then send the request to the service endpoint along with the JSESSIONID.
In this case i want to work without redirections.
From the client-side I want to send a Header with an API-Key and an HMAC directly to the service endpoint, and then on the server authenticate the requester against these parameters and then proceed to process the request. I dont want to use sessions (similar to what the BasicAuthenticationFilter does).
To summarize, i want to be able to authenticate and process the request in one shot.
So I created my own filter:
public class HMACFilter extends BasicAuthenticationFilter {
private static final Logger LOGGER = Logger.getLogger(HMACFilter.class);
public static final String HMAC_SECURITY_HEADER_APIKEY_FIELD = "VU-API-Key";
public static final String HMAC_SECURITY_HEADER_HMAC_FIELD = "VU-HMAC";
public static final String HMAC_SECURITY_HEADER_TIMESTAMP_FIELD = "VU-Timestamp";
public static final String HMAC_SECURITY_URL_AFFILIATEID_FIELD = "affiliateid";
public HMACFilter() {
//super("/api_security");
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
String headerApiKey = obtainApiKey(request);
String headerHmac = obtainHMACSignature(request);
String headerTimestamp = obtainRequestDate(request);
int requestAffiliateId = obtainAffiliateId(request);
String requestMessage = obtainMessage(request);
VUHMACCredentials credentials = new VUHMACCredentials();
if (headerHmac == null || headerApiKey == null || headerTimestamp == null) {
throw new AuthenticationServiceException("Authentication Headers cannot be null");
}
credentials.setApiKey(headerApiKey);
credentials.setHMACSignature(headerHmac);
credentials.setTimestamp(Long.valueOf(headerTimestamp));
VUCustomHMACAuthenticationToken authRequest = new VUCustomHMACAuthenticationToken(requestAffiliateId, credentials, requestMessage);
try{
Authentication authResult = this.getAuthenticationManager().authenticate(authRequest);
SecurityContextHolder.getContext().setAuthentication(authResult);
} catch (AuthenticationException var12) {
SecurityContextHolder.clearContext();
this.onUnsuccessfulAuthentication(request, response, var12);
return;
}
chain.doFilter(request, response);
}
And the security.xml:
<security:http entry-point-ref="apiAuthenticationEntryPoint" pattern="/rest/api/**">
<security:intercept-url pattern="/rest/api/**"
access="ROLE_APIUSER" />
<security:custom-filter position="FIRST"
ref="hmacFilter" />
</security:http>
<bean id="hmacFilter" class="com.vu.acs.edge.external.api.security.HMACFilter"
p:authenticationEntryPoint-ref="apiAuthenticationEntryPoint"
p:authenticationManager-ref="hmacAuthenticationManager"/>
<bean id="hmacAuthenticationManager" class="com.vu.acs.edge.external.spring.security.VUCustomHMACAuthenticationManager"
/>
This xmls overrides the j_spring_security_check url and authenticates on every URL that matches the pattern /rest/api/**
The issue here is that spring security is authenticating and returning a 200 RC but not calling the rest service. So, how can i make the framework to call the rest services after authentication? I need kinda what the SavedRequestAwareAuthenticationSuccessHandler does but without using redirections, everything should be done with just one request from the client.
JSESSIONID changing at time outs and may occure strange problem such well known session fixation issues!
I think instead of using JSESSIONID ,for your case using cookie is better choice ,you can open cors filter authantication and send via header cookie .store data such specific user id (encrypted) and validate in method body but not user pass !. for use cookie you can use :
<session-config>
<session-timeout>10</session-timeout>
<tracking-mode>COOKIE</tracking-mode>
</session-config>
strating from tomcat 7 servlet 3.
I am running into issue in forming the correct api statement for JAVA in calling the Blogger API.
I have tested my statement via the Google Cloud Console and it works but does not work in my code. I am using Google App Engine and have been authorized to use Blogger. The authorization is also tied to the account running Google App Engine.
Any ideas would be helpfull.. have tried many things over the weekend.
Thanks
Request
GET https://www.googleapis.com/blogger/v3/blogs/7676001971884966148/posts?key= {YOUR_API_KEY}
Authorization: Bearer ya29.1.AADtN_Vd7lKj8Xy3KbZ1veJjjjv712Nc1erLY2dmAK3gorNilVd0652vnqrrovfuLfSKkQ
X-JavaScript-User-Agent: Google APIs Explorer
Response
200 OK
- Show headers -
{
"kind": "blogger#postList",
"nextPageToken": "CgkIChjim-ftqygQhIKb6_zjqMNq",
"items": [
{
etc.....
My Code
public class BloggerHandler
{
public static final Logger log = Logger.getLogger(BloggerHandler.class.getName());
public void testCreds() throws Exception {
try{
ArrayList<String> scopes = new ArrayList<String>();
scopes.add("https://www.googleapis.com/auth/blogger");
scopes.add("https://www.googleapis.com/auth/blogger.readonly");
AppIdentityService appIdentity = AppIdentityServiceFactory.getAppIdentityService();
AppIdentityService.GetAccessTokenResult accessToken = appIdentity.getAccessToken(scopes);
// The token asserts the identity reported by appIdentity.getServiceAccountName()
JSONObject request = new JSONObject();
//request.put("maxPosts", "1");
//request.put("view", "AUTHOR");
log.info("request!!!" + request);
URL url = new URL("https://www.googleapis.com/blogger/v3/blogs/7676001971884966148/posts?");
log.info("URL:" + url);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("GET");
connection.addRequestProperty("Content-Type", "application/json");
connection.addRequestProperty("Authorization", "OAuth" + accessToken.getAccessToken());
log.info("Con!!" + connection);
OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
request.write(writer);
writer.close();
log.info("connection:" + connection.getResponseCode());
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
// Note: Should check the content-encoding.
JSONTokener response_tokens = new JSONTokener(connection.getInputStream());
JSONObject response = new JSONObject(response_tokens);
log.info("resp:" + response.get("title"));
} // end if
else {
throw new Exception();
}// end else
} // end try
catch (Exception e) {
// Error handling elided.
log.info("ex:" + e);
}
// end catch
}// end void
}// end class
After a few long nights I was able to figure out how to access the Google Blogger API from a GAE project.
There are a couple of key things that I did that may help aid you.
In your Google Appe Engine project make sure that it is linked to the Google API Console. IN the GAE Admin project screen you should see a Google APIs Console Project Number:XX
In the Google API Cloud console make sure you are authorized for Blogger (or whatever cloud API you want to use). Create a project (website.. etc) and copy down the API string that it gives you.
Once you have that API string the code below should get you started with base connection.
The code below should return a "200" connection with the stats of the current blog you are trying to reach. From here you can expand upon the API.
// On a side note I know there is a way to read the API from Google Cloud so it does not have to be part of the code. Still working on that workflow.
import java.util.logging.Logger;
import java.util.Arrays;
import com.google.api.client.googleapis.extensions.appengine.auth.oauth2.AppIdentityCredential;
import com.google.api.services.blogger.Blogger;
import com.google.api.services.blogger.Blogger.Blogs.GetByUrl;
import com.google.api.services.blogger.Blogger.Posts.List;
import com.google.api.services.blogger.BloggerScopes;
import com.google.api.services.blogger.model.Blog;
import com.google.api.services.blogger.model.Post;
import com.google.api.services.blogger.model.PostList;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.extensions.appengine.http.UrlFetchTransport;
import java.io.IOException;
public class BlogHandler
{
public Blogger blogger = null;
public Blog blog;
public java.util.List<Post> posts;
public static final Logger log = Logger.getLogger(EngineParseFeed.class.getName());
static final String API_KEY = "{Your GOOGLE CLOUD API}";
public BlogHandler() {}
public void setupService () throws IOException {
AppIdentityCredential credential = null;
credential = new AppIdentityCredential(Arrays.asList(BloggerScopes.BLOGGER)); // Add your scopes here
this.blogger = new Blogger.Builder(new UrlFetchTransport(), new JacksonFactory(), credential).setApplicationName("trivalAPPName").build();
}
public void executeGetBlogByUrl (String url) throws IOException {
GetByUrl request = blogger.blogs().getByUrl( url );
this.blog = request.setKey(API_KEY).execute();
log.info ("Blog" + this.blog);
}
I managed to get the Blogger API working properly on Google App Engine by using two servlets and modelling them after the examples on this page: https://developers.google.com/google-apps/tasks/oauth-authorization-callback-handler. The example code is out of date and uses some kind of deprecated draft10 library.
Here's the working version for the servlet that posts to Blogger:
public class BloggerServlet
{
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
public static GoogleAuthorizationCodeFlow flow;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException
{
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Entity OAuthTokenEntity;
OAuthProperties oauthProperties = new OAuthProperties();
String OAuthAccessToken, OAuthRefreshToken;
try
{
OAuthTokenEntity = datastore.get(KeyFactory.createKey("OAuthTokenEntity","OA"));
OAuthAccessToken = OAuthTokenEntity.getProperty("OAuthAccessToken").toString();
OAuthRefreshToken = OAuthTokenEntity.getProperty("OAuthRefreshToken").toString();
}
catch(EntityNotFoundException e)
{
Collection<String> scopes = Arrays.asList(BloggerScopes.BLOGGER);
flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY,
CLIENT_ID, CLIENT_SECRET, scopes)
.setAccessType("offline")
.setApprovalPrompt("auto").build();
String url = flow.newAuthorizationUrl()
.setRedirectUri(OAuthCodeCallbackHandlerServlet.getOAuthCodeCallbackHandlerUrl(request))
.build();
response.sendRedirect("http://OAuthCodeCallbackHandlerServlet");
return;
}
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setClientSecrets(CLIENT_ID, CLIENT_SECRET)
.build();
credential.setAccessToken(OAuthAccessToken);
credential.setRefreshToken(OAuthRefreshToken);
Blogger blog = new Blogger.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
.setApplicationName("APP_NAME").setHttpRequestInitializer(credential).build();
}
}
And here is the working version of the Servlet that handles the callback:
public class OAuthCodeCallbackHandlerServlet
{
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String[] code = request.getParameterValues("code");
GoogleTokenResponse tokenResponse = BloggerServlet.flow.newTokenRequest(code[0]).setRedirectUri(getOAuthCodeCallbackHandlerUrl(request)).execute();
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Entity OAuthTokenEntity = new Entity("OAuthTokenEntity","OA");
OAuthTokenEntity.setProperty("OAuthAccessToken", tokenResponse.getAccessToken());
OAuthTokenEntity.setProperty("OAuthRefreshToken",tokenResponse.getRefreshToken());
datastore.put(OAuthTokenEntity);
response.sendRedirect("http://BloggerServlet");
}
public static String getOAuthCodeCallbackHandlerUrl(HttpServletRequest request)
{
StringBuilder oauthURL = new StringBuilder();
oauthURL.append(request.getScheme() + "://");
oauthURL.append(request.getServerName());
oauthURL.append(request.getServerPort() == 80 ? "" : ":" + request.getServerPort());
oauthURL.append(request.getContextPath());
oauthURL.append(URL_MAPPING);
oauthURL.append(request.getPathInfo() == null ? "" : request.getPathInfo());
return oauthURL.toString();
}
}
I have been working on this and my conclusion is:
To make a request for a blog or post, as long as they are public, you don't have to authenticate, i.e. you don't need OAuth for this kind of requests.Still, you have to identify your application and you do that with the APP_KEY. As stated here, not any application can use the Blogger API, it has to be authorized and for this reason is this key needed.
Based on 1., you dont need to create an AppIdentityCredential. I just set the third parameter of Builder to null. I tried using your code but it didn't work.
I still have a question and probably is the same iamkhova has. I think an app can authenticate itself with OAuth using the AppIdentityCredential for GAE apps. But how can you tell google that my application is owner of the blog I am trying to access? I think this is the reason of getting this 401.