Google Glass GDK authentication using Java - authentication

I was testing the authentication for my GDK Glassware :
This is my code:
===================================================================================
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.List;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.Lists;
import com.google.api.services.mirror.Mirror;
import com.google.api.services.mirror.model.Account;
import com.google.api.services.mirror.model.AuthToken;
public class InsertAccountWithJava {
/** Email of the Service Account */
private static final String SERVICE_ACCOUNT_EMAIL =
"540223414844-tj91ijj3u9**********#developer.gserviceaccount.com ";
/** Path to the Service Account's Private Key file */
private static final String SERVICE_ACCOUNT_PKCS12_FILE_PATH =
"C:/Users/Yuan/Desktop/eyenotes-847a787fecec.p12";
/** The account type, usually based on your company or app's package. */
private static final String ACCOUNT_TYPE = "com.myapplication";
/** The Mirror API scopes needed to access the API. */
private static final List<String> MIRROR_ACCOUNT_SCOPES =Arrays.asList(
"https://www.googleapis.com/auth/glass.thirdpartyauth");
public static void main(String[] args) {
try {
Mirror mirror = getMirrorService();
createAccount(mirror, "6164da1******", "zhongmeiCM", "com.myapplication", "747dab3e60dd8cd45595767580040538c8a29fcf");
} catch (GeneralSecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
/**
* Build and returns a Mirror service object authorized with the service accounts.
*
* #return Mirror service object that is ready to make requests.
*/
public static Mirror getMirrorService() throws GeneralSecurityException, IOException, URISyntaxException {
HttpTransport httpTransport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(MIRROR_ACCOUNT_SCOPES)
.setServiceAccountPrivateKeyFromP12File(
new java.io.File(SERVICE_ACCOUNT_PKCS12_FILE_PATH))
.build();
Mirror service = new Mirror.Builder(httpTransport, jsonFactory, null)
.setHttpRequestInitializer(credential).build();
return service;
}
/**
* Creates an account and causes it to be synched up with the user's Glass.
* This example only supports one auth token; modify it if you need to add
* more than one, or to add features or user data or the password field.
*
* #param mirror the service returned by getMirrorService()
* #param userToken the user token sent to your auth callback URL
* #param accountName the account name for this particular user
* #param authTokenType the type of the auth token (chosen by you)
* #param authToken the auth token
*/
public static void createAccount(Mirror mirror, String userToken, String accountName,
String authTokenType, String authToken) {
try {
Account account = new Account();
List<AuthToken> authTokens = Lists.newArrayList();
AuthToken authToken1 = new AuthToken().setType(authTokenType).setAuthToken(authToken);
authTokens.add(authToken1);
account.setAuthTokens(authTokens);
account.setPassword("123456");
mirror.accounts().insert(
userToken, ACCOUNT_TYPE, accountName, account).execute();
} catch (IOException e) {
e.printStackTrace();
}
}
}
=====================================================================================
But I get an exception:
com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"message" : "Invalid Value",
"reason" : "invalid"
}, {
"domain" : "global",
"message" : "Invalid Value",
"reason" : "invalid"
} ],
"message" : "Invalid Value"
}
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 TestInsert.createAccount(TestInsert.java:92)
at TestInsert.main(TestInsert.java:39)
Question 1.please tell me how to generate the parameter:authToken,and what's the difference between authToken and accessToken ?
Question 2.what's the problem with my test code ? When can i get the Exception above.

authToken is an arbitrary String that you can define yourself. accessToken, on the other hand, is a token given to you during the authentication process. It is passed to the Google Mirror API to give your application access to user data.
Refer to the selected answer to this post.

Related

Unable to initialize AdaptersAPI Object in MobileFirst V8.0 adapter which is leading to NullPointerException

I am developing the adapter in MFP V8. Below is my code to validate username and password:
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import com.ibm.mfp.adapter.api.AdaptersAPI;
import com.ibm.mfp.adapter.api.ConfigurationAPI;
import com.ibm.mfp.security.checks.base.UserAuthenticationSecurityCheck;
import com.ibm.mfp.server.registration.external.model.AuthenticatedUser;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
#Api(value = "Sample Adapter Resource")
#Path("/resource")
public class UserValidationSecurityCheck extends UserAuthenticationSecurityCheck{
private String displayName;
private String errorMsg;
private HashMap<String,Object> adapterReponse = null;
#Context
AdaptersAPI adaptersAPI;
#Override
protected AuthenticatedUser createUser() {
return new AuthenticatedUser(displayName, displayName, this.getName(),adapterReponse);
}
#Override
protected boolean validateCredentials(Map<String, Object> credentials) {
if(credentials!=null && credentials.containsKey("username") && credentials.containsKey("password")){
if (credentials.get("username")!=null && credentials.get("password")!=null) {
String username = credentials.get("username").toString();
String password = credentials.get("password").toString();
if (username.equals(password)) {
JSONObject loginParams = new JSONObject();
loginParams.put("username", username);
loginParams.put("password", password);
HttpUriRequest httpUriRequest = adaptersAPI.createJavascriptAdapterRequest("LoginAndWeeklyCertAdapter1", "login", loginParams);
try {
HttpResponse httpResponse = adaptersAPI.executeAdapterRequest(httpUriRequest);
adapterReponse = adaptersAPI.getResponseAsJSON(httpResponse);
System.out.println(adapterReponse.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
} else {
errorMsg = "Wrong Credentials";
}
}
}
else{
errorMsg = "Credentials not set properly";
}
return false;
}
public boolean isLoggedIn(){
return getState().equals(STATE_SUCCESS);
}
public AuthenticatedUser getRegisteredUser() {
return registrationContext.getRegisteredUser();
}
#Override
protected Map<String, Object> createChallenge() {
Map<String, Object> challenge = new HashMap<String, Object>();
challenge.put("errorMsg", errorMsg);
challenge.put("remainingAttempts", getRemainingAttempts());
return challenge;
}
#ApiOperation(value = "Returns 'Hello from resource'", notes = "A basic example of a resource returning a constant string.")
#ApiResponses(value = { #ApiResponse(code = 200, message = "Hello message returned") })
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getResourceData() {
// log message to server log
logger.info("Logging info message...");
return "Hello from resource";
}
}
When I am submitting the challenge answer I am getting NullPointerException in following line:
HttpUriRequest httpUriRequest = adaptersAPI.createJavascriptAdapterRequest("LoginAndWeeklyCertAdapter1", "login");
because adaptersAPI is null. Do I have to do any extra configuration in order to make that work? How can I initialize AdaptersAPI object?
Note: The login method and the security check both are in same adapter.
Update
I investigated more of time into it and updated the code to given above and observed the following:
1. When validateCredentials() is getting called after submitting the challenge response then I am getting null value in AdapterAPI object.
2. Where as, when I am calling the getResourceData() using the mobilefirst swagger tool then I am getting an object of AdapterAPI.
We cannot inject the adapters API into a security check object(by design - not a bug). The only way we can go is to extract the logic from Adapter into a Java code, without using adapters API.

GDK Authentication mirror.accounts.insert.execute()

This is the test code for authentication and insert Account :
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.Lists;
import com.google.api.services.mirror.Mirror;
import com.google.api.services.mirror.Mirror.Accounts.Insert;
import com.google.api.services.mirror.model.Account;
import com.google.api.services.mirror.model.AuthToken;
public class InsertAccountWithJava {
private static final String SERVICE_ACCOUNT_EMAIL ="540223414844-tqpuqtbv07klpuuk3q04qha0g88094uu#developer.gserviceaccount.com";
private static final String SERVICE_ACCOUNT_PKCS12_FILE_PATH ="C:/Users/Yuan/Desktop/eyenotes-db176c40f51f.p12";
private static final String ACCOUNT_TYPE = "com.eyenotes";
private static final Collection<String> MIRROR_ACCOUNT_SCOPES =Arrays.asList("https://www.googleapis.com/auth/glass.thirdpartyauth");
private static String userToken = "6164da1732ea09b4";
private static String accountName="zhongmei";
private static String authTokenType ="eyenotes";
private static String authToken="myauthtoken";
public static void main(String[] args) {
try {
Mirror mirror = getMirrorService();
createAccount(mirror, userToken, accountName, authTokenType, authToken);
} catch (GeneralSecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
public static Mirror getMirrorService() throws GeneralSecurityException, IOException, URISyntaxException {
HttpTransport httpTransport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(MIRROR_ACCOUNT_SCOPES)
.setServiceAccountPrivateKeyFromP12File(
new java.io.File(SERVICE_ACCOUNT_PKCS12_FILE_PATH))
.build();
Mirror service = new Mirror.Builder(httpTransport, jsonFactory, null)
.setHttpRequestInitializer(credential).build();
return service;
}
public static void createAccount(Mirror mirror, String userToken, String accountName,
String authTokenType, String authToken) {
try {
Account account = new Account();
List<AuthToken> authTokens = Lists.newArrayList();
AuthToken authToken1 = new AuthToken().setType(authTokenType).setAuthToken(authToken);
authTokens.add(authToken1);
account.setAuthTokens(authTokens);
account.setPassword("zmyl123456");
Insert insert = mirror.accounts().insert(userToken, ACCOUNT_TYPE, accountName, account);
insert.execute();
} catch (IOException e) {
e.printStackTrace();
}
}
}
============================================================================
I got the exception below:
============================================================================
Fabruary 06, 2015 1:42:21 pm com.google.api.client.googleapis.services.AbstractGoogleClient <init>
warning: Application name is not set. Call Builder#setApplicationName.
com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"message" : "Invalid Value",
"reason" : "invalid"
}, {
"domain" : "global",
"message" : "Invalid Value",
"reason" : "invalid"
} ],
"message" : "Invalid Value"
}
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 com.ccfsz.service.InsertAccountWithJava.createAccount(InsertAccountWithJava.java:81)
at com.ccfsz.service.InsertAccountWithJava.main(InsertAccountWithJava.java:30)
=====================================================================================
Parameters explaination:
SERVICE_ACCOUNT_EMAIL:Generated when i create new Client ID in developer console.
SERVICE_ACCOUNT_PKCS12_FILE_PATH:Generated when i create new Client ID in developer console.
ACCOUNT_TYPE:I provided to google when submit my application.
userToken:I got from the authentication url provided to google.
accountName,authTokenType and authToken is arbitrary String.
2.Before i execute the code above ,do i need to do anything else?
3.I can get google authorization page through (https://accounts.google.com/o/oauth2/auth?response_type=code&scope=https://www.googleapis.com/auth/glass.timeline+https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile&access_type=offline&approval_prompt=force&include_granted_scopes=true&client_id=540223414844-jjhqevuu0rhmoa3u6gkd05g7v29i89kk.apps.googleusercontent.com&redirect_uri=http://www.eyenotes.com/glass/login.php) using web application .how can i get this page and get permission using service account.
4.Who can give me any hint ? Sometimes,after execute test code above,i got nothing.in this case,according to their source code , this means i got a Http response code between 200 and 300, but i never get Accounts on my glass device which accountType equals "com.eyenotes".sometimes it return 400 with the excepiton above.
who have any idea,please tell me.I am very appreciate.
The userToken may not be the same every single time. Instead of hard-coding it to a String, I would get the token sent to your sign-in page during the authentication process.
Also, the error says you will need to call setApplicationName(String) on your Mirror object in the following code:
Mirror service = new Mirror.Builder(httpTransport, jsonFactory, null)
.setHttpRequestInitializer(credential).build();
Refer to line 38 here.

Google API oAuth2.0 -- Consumer is not registered

I have oAuth 2.0 code that works with other services (such as LinkedIn and Facebook) but niot Google.
The code fails with 'Consumer is not registered'. It certainly is. That is if this error means what I think it means but I do have the following in https://code.google.com/apis/console.
a project,
and a valid CLIENT_ID entry
Client ID: 107***********4ek7fl.apps.googleusercontent.com
Client secret: Q6KbA**********FRbL
Redirect URIs: urn:ietf:wg:oauth:2.0:oob, htp://{localhost}
The failure occurs when the request is first sent to https://accounts.google.com/o/oauth2/auth
No page is displayed asking the user to authenticate, the Google server returns "Consumer is not registered" in the response body.
I had a similar problem. I am using the scribe oauth java library, https://github.com/fernandezpablo85/scribe-java. It's support of Google is only oauth 1.0, so I had to write my own class that extends org.scribe.builder.api.DefaultApi20 instead of org.scribe.builder.api.DefaultApi10a.
basically here you are:
package org.scribe.builder.api;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.scribe.exceptions.OAuthException;
import org.scribe.extractors.AccessTokenExtractor;
import org.scribe.model.OAuthConfig;
import org.scribe.model.OAuthConstants;
import org.scribe.model.OAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.Token;
import org.scribe.model.Verb;
import org.scribe.model.Verifier;
import org.scribe.oauth.OAuth20ServiceImpl;
import org.scribe.oauth.OAuthService;
import org.scribe.utils.OAuthEncoder;
import org.scribe.utils.Preconditions;
/**
* Google OAuth2.0
* Released under the same license as scribe (MIT License)
* #author houman001
* This code borrows from and modifies changes made by #yincrash
* #author yincrash
*
*/
public class Google2Api extends DefaultApi20 {
private static final String AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=%s&redirect_uri=%s";
private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=%s";
private static final String SUFFIX_OFFLINE = "&" + OAuthConstants.ACCESS_TYPE + "=" + OAuthConstants.ACCESS_TYPE_OFFLINE
+ "&" + OAuthConstants.APPROVAL_PROMPT + "=" + OAuthConstants.APPROVAL_PROMPT_FORCE;
#Override
public String getAccessTokenEndpoint() {
return "https://accounts.google.com/o/oauth2/token";
}
#Override
public AccessTokenExtractor getAccessTokenExtractor() {
return new AccessTokenExtractor() {
public Token extract(String response) {
Preconditions.checkEmptyString(response, "Response body is incorrect. Can't extract a token from an empty string");
Matcher matcher = Pattern.compile("\"access_token\" : \"([^&\"]+)\"").matcher(response);
if (matcher.find())
{
String token = OAuthEncoder.decode(matcher.group(1));
String refreshToken = "";
Matcher refreshMatcher = Pattern.compile("\"refresh_token\" : \"([^&\"]+)\"").matcher(response);
if (refreshMatcher.find())
refreshToken = OAuthEncoder.decode(refreshMatcher.group(1));
Date expiry = null;
Matcher expiryMatcher = Pattern.compile("\"expires_in\" : ([^,&\"]+)").matcher(response);
if (expiryMatcher.find())
{
int lifeTime = Integer.parseInt(OAuthEncoder.decode(expiryMatcher.group(1)));
expiry = new Date(System.currentTimeMillis() + lifeTime * 1000);
}
Token result = new Token(token, refreshToken, expiry, response);
return result;
}
else
{
throw new OAuthException("Response body is incorrect. Can't extract a token from this: '" + response + "'", null);
}
}
};
}
#Override
public String getAuthorizationUrl(OAuthConfig config) {
// Append scope if present
if (config.hasScope()) {
String format = config.isOffline() ? SCOPED_AUTHORIZE_URL + SUFFIX_OFFLINE : SCOPED_AUTHORIZE_URL;
return String.format(format, config.getApiKey(),
OAuthEncoder.encode(config.getCallback()),
OAuthEncoder.encode(config.getScope()));
} else {
String format = config.isOffline() ? AUTHORIZE_URL + SUFFIX_OFFLINE : AUTHORIZE_URL;
return String.format(format, config.getApiKey(),
OAuthEncoder.encode(config.getCallback()));
}
}
#Override
public Verb getAccessTokenVerb() {
return Verb.POST;
}
#Override
public OAuthService createService(OAuthConfig config) {
return new GoogleOAuth2Service(this, config);
}
private static class GoogleOAuth2Service extends OAuth20ServiceImpl {
private DefaultApi20 api;
private OAuthConfig config;
public GoogleOAuth2Service(DefaultApi20 api, OAuthConfig config) {
super(api, config);
this.api = api;
this.config = config;
}
#Override
public Token getAccessToken(Token requestToken, Verifier verifier) {
OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint());
switch (api.getAccessTokenVerb()) {
case POST:
request.addBodyParameter(OAuthConstants.CLIENT_ID, config.getApiKey());
// API Secret is optional
if (config.getApiSecret() != null && config.getApiSecret().length() > 0)
request.addBodyParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret());
if (requestToken == null) {
request.addBodyParameter(OAuthConstants.CODE, verifier.getValue());
request.addBodyParameter(OAuthConstants.REDIRECT_URI, config.getCallback());
request.addBodyParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.GRANT_TYPE_AUTHORIZATION_CODE);
} else {
request.addBodyParameter(OAuthConstants.REFRESH_TOKEN, requestToken.getSecret());
request.addBodyParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.GRANT_TYPE_REFRESH_TOKEN);
}
break;
case GET:
default:
request.addQuerystringParameter(OAuthConstants.CLIENT_ID, config.getApiKey());
// API Secret is optional
if (config.getApiSecret() != null && config.getApiSecret().length() > 0)
request.addQuerystringParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret());
request.addQuerystringParameter(OAuthConstants.CODE, verifier.getValue());
request.addQuerystringParameter(OAuthConstants.REDIRECT_URI, config.getCallback());
if(config.hasScope()) request.addQuerystringParameter(OAuthConstants.SCOPE, config.getScope());
}
Response response = request.send();
return api.getAccessTokenExtractor().extract(response.getBody());
}
}
}
from https://raw.githubusercontent.com/codolutions/scribe-java/master/src/main/java/org/scribe/builder/api/Google2Api.java

getting an error while acessing google calendar API. Null pointer exception

I get a error message below .. when I run the code for hitting google Calendar API.There is a client_secrets.json file that has the credentials (client ID and redirectID) . I unzipped and ran the package google gave, it successfully runs and I could see that by a "Success,Add code here"message. after which it throws me a nullpointer exception for which I'm not sure what has gone wrong.please help. Downloaded the sample project from here https://developers.google.com/api-client-library/java/apis/calendar/v3
May 01, 2014 12:59:42 PM com.google.api.client.util.store.FileDataStoreFactory setPermissionsToOwnerOnly
WARNING: unable to change permissions for everybody: C:\Users\Aishwarya Anand\.store\calendar_sample
May 01, 2014 12:59:42 PM com.google.api.client.util.store.FileDataStoreFactory setPermissionsToOwnerOnly
WARNING: unable to change permissions for owner: C:\Users\Aishwarya Anand\.store\calendar_sample
Success! Now add code here.
inserting event ...
Aish Event from the new Google API
2014-05-01T16:59:42.545Z
2014-05-01T17:59:42.545Z
java.lang.NullPointerException
at com.google.api.client.repackaged.com.google.common.base.Preconditions.checkNotNull(Preconditions.java:191)
at com.google.api.client.util.Preconditions.checkNotNull(Preconditions.java:127)
at com.google.api.client.json.jackson2.JacksonFactory.createJsonParser(JacksonFactory.java:96)
at com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:85)
at com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:81)
at com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:88)
at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:287)
at com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:307)
at com.google.api.client.auth.oauth2.Credential.executeRefreshToken(Credential.java:570)
at com.google.api.client.auth.oauth2.Credential.refreshToken(Credential.java:489)
at com.google.api.client.auth.oauth2.Credential.intercept(Credential.java:217)
at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:859)
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 com.google.api.services.samples.calendar.cmdline.CalendarSample.insertEventTest(CalendarSample.java:170)
at com.google.api.services.samples.calendar.cmdline.CalendarSample.main(CalendarSample.java:116)
Class here,
package com.google.api.services.samples.calendar.cmdline;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.DateTime;
import com.google.api.client.util.store.DataStoreFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.calendar.Calendar;
import com.google.api.services.calendar.CalendarScopes;
import com.google.api.services.calendar.model.*;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Date;
import java.util.TimeZone;
public class CalendarSample {
/**
* Be sure to specify the name of your application. If the application name is {#code null} or
* blank, the application will log a warning. Suggested format is "MyCompany-ProductName/1.0".
*/
private static final String APPLICATION_NAME = "Caritas-CalendarAPI/1.0";
/** Directory to store user credentials. */
private static final java.io.File DATA_STORE_DIR =
new java.io.File(System.getProperty("user.home"), ".store/calendar_sample");
/**
* Global instance of the {#link DataStoreFactory}. The best practice is to make it a single
* globally shared instance across your application.
*/
private static FileDataStoreFactory dataStoreFactory;
/** Global instance of the JSON factory. */
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
/** Global instance of the HTTP transport. */
private static HttpTransport httpTransport;
#SuppressWarnings("unused")
private static Calendar client;
/** Authorizes the installed application to access user's protected data. */
private static Credential authorize() throws Exception {
// load client secrets
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY,
new InputStreamReader(CalendarSample.class.getResourceAsStream("/client_secrets.json")));
// Set up authorization code flow.
// Ask for only the permissions you need. Asking for more permissions will
// reduce the number of users who finish the process for giving you access
// to their accounts. It will also increase the amount of effort you will
// have to spend explaining to users what you are doing with their data.
// Here we are listing all of the available scopes. You should remove scopes
// that you are not actually using.
Set<String> scopes = new HashSet<String>();
scopes.add(CalendarScopes.CALENDAR);
scopes.add(CalendarScopes.CALENDAR_READONLY);
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
httpTransport, JSON_FACTORY, clientSecrets, scopes)
.setDataStoreFactory(dataStoreFactory)
.build();
// authorize
return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
}
public static void main(String[] args) {
try {
// initialize the transport
httpTransport = GoogleNetHttpTransport.newTrustedTransport();
// initialize the data store factory
dataStoreFactory = new FileDataStoreFactory(DATA_STORE_DIR);
// authorization
Credential credential = authorize();
// set up global Calendar instance
client = new Calendar.Builder(httpTransport, JSON_FACTORY, credential).setApplicationName(APPLICATION_NAME).build();
System.out.println("Success! Now add code here.");
insertEventTest();
//getEventList();
//getEventTest();
//sertEventTest();
} catch (IOException e) {
System.err.println(e.getMessage());
} catch (Throwable t) {
t.printStackTrace();
}
System.exit(1);
}
private static void getEventList() throws IOException {
String pageToken = null;
do {
Events events = client.events().list("primary").setPageToken(pageToken).execute();
List<Event> items = events.getItems();
for (Event event : items) {
System.out.println(" id: " + event.getId() + "; summary: " + event.getSummary());
}
pageToken = events.getNextPageToken();
} while (pageToken != null);
}
private static void getEventTest() throws IOException {
String eventId = "idkoi3c55lvegs2ev333u28grk5c";
Event event = client.events().get("primary", eventId).execute();
System.out.println(event.getSummary());
}
private static void insertEventTest() throws IOException {
System.out.println("inserting event ...");
Event event = new Event();
event.setSummary("Aish Event from the new Google API");
event.setLocation("Somewhere");
System.out.println(event.getSummary());
Date startDate = new Date();
Date endDate = new Date(startDate.getTime() + 3600000);
DateTime start = new DateTime(startDate, TimeZone.getTimeZone("UTC"));
event.setStart(new EventDateTime().setDateTime(start));
System.out.println(start.toStringRfc3339());
DateTime end = new DateTime(endDate, TimeZone.getTimeZone("UTC"));
event.setEnd(new EventDateTime().setDateTime(end));
System.out.println(end.toStringRfc3339());
Event createdEvent = client.events().insert("primary", event).execute();
System.out.println("event id" + createdEvent.getId());
}
}

Safari Push Notifications

I would like to implement push notifications for my website (obviously only in compatible browser as Safari 7).
I have read the Apple documentation and I have successfully created my package containing my icon.iconset, my certificate.p12, manifest.json and a website.json.
Now I would like to ask the permission to the user when I first visit the website. If he allows it, I should send the package.
Everything is pretty clear but I don't know how to go on.
How do I create my push package out of my files? How do I precisely sign it? The package should be always the same so I could sign it on my mac and upload to my server only one package.
If you have experience with this technology, please let me know :)
I have successfully create push package to ask permission on safari web push notifications using REST API in java. Also I have follow steps which provide by Apple officials on there sites.
Please follow below steps to create push package.
Create your web push notification P12 certificate from your apple account.
Create your REST API using https for pushPackage which contain icon.iconset, your certificate.p12, manifest.json and a website.json. p12 certificate must be web push notification certificate.
How to create push package:- Please refer below java code. I have create push package using java servlet. which provide 2 web service end points.
v1/pushpackage/webpushID
v1/log
Servlet which process your push package request which send by safari push notification method
SafariPushPakageAPI.java
/*
Push Package REST API handler
*/
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import com.safari.Packager;
public class SafariPushPakageAPI extends HttpServlet{
/**
*
*/
private static final long serialVersionUID = 1L;
public static String ServerPath = null;
private static final String REQUEST_PERMISSION = "/v1/pushPackages/YOUR_WEB_PUSH_ID";
private static final String REQUEST_ERRORLOG = "/v1/log";
#Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doRequest(request, response);
}
// /v1/pushPackages/webpushID
// /v1/log
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doRequest(request, response);
}
private void doRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("===>> SAFARI PUSH NOTIFICATION REQUEST");
String path = request.getPathInfo();
System.out.println("PATH ===>> "+path);
if(path == null){
doRequestPermission(request, response);
}else if (path.equalsIgnoreCase(REQUEST_PERMISSION)){
doRequestPermission(request, response);
}else if (path.equalsIgnoreCase(REQUEST_ERRORLOG)){
doRequestShowErrorLog(request, response);
}else{
doRequestPermission(request, response);
}
}
private void doRequestPermission(HttpServletRequest request,HttpServletResponse response) {
try{
System.out.println("INSIDE REQUEST PERMISSION ==>>>");
System.out.println(IOUtils.toString(request.getReader()));
String authToken = StringUtils.isBlank(request.getParameter("token")) ? "UserTokenRT124DFGH" : StringUtils.trimToEmpty(request.getParameter("token"));
System.out.println("=>>>>>>>>>> USER TOKEN =>>>>>>>>>> "+authToken);
#SuppressWarnings("deprecation")
String packagePath =request.getRealPath("pushPackage.raw/icon.iconset/"); // LOCATION WHERE YOUR PUSH PACKAGE FOLDER CONTAIN LOGOS AND website.json file
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment;filename=\"pushpackage.zip\"");
OutputStream out = response.getOutputStream();
out.write(Packager.createPackageFile(authToken,packagePath));
response.flushBuffer();
}catch(IOException ioe){
ioe.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
private void doRequestShowErrorLog(HttpServletRequest request,HttpServletResponse response) {
try{
System.out.println("ERROR LOG STARTED");
System.out.println(IOUtils.toString(request.getReader()));
System.out.println("END");
}catch(Exception e){
e.printStackTrace();
}
}
}
Packager.java
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONObject;
/**
*
* #author Ritesh
*/
public class Packager {
final static String CERTIFICATE_PATH="PATH TO YOUR 12 CERTIFICATE";
final static String CERTIFICATE_PASS="PASSWORD";
static String getJSON(String authenticationToken) throws Exception {
JSONObject obj = new JSONObject();
obj.put("websiteName", "WEB SITE NAME");
obj.put("websitePushID", "WEB PUSH ID");
obj.put("allowedDomains", new JSONArray());
obj.getJSONArray("allowedDomains").put("https://TEST.EXAMPLE.net");//LIST OF DOMAINS ALLOW
obj.put("urlFormatString", "https://TEST.EXAMPLE.net/%#");
obj.put("authenticationToken", authenticationToken);
obj.put("webServiceURL", "https://API.EXAMPLE.COM");//callback URL WITHOUT WEB SERVICE ENDPOINT NAME
return obj.toString();
}
public static byte[] createPackageFile(String authenticationToken, String path) throws Exception {
System.out.println("packaging safari file with token: " + authenticationToken);
ZipHandler zip = new ZipHandler();
File dir = new File(path);
for (File file : dir.listFiles()) {
InputStream is = new FileInputStream(file);
byte[] bytes = IOUtils.toByteArray(is);
zip.addFile("icon.iconset", file.getName(),bytes );
}
zip.addFile("", "website.json", getJSON(authenticationToken).getBytes());
byte[] manifest = zip.manifest();
zip.addFile("", "manifest.json", manifest);
zip.addFile("", "signature", sign(manifest));
return zip.getBytes();
}
static byte[] sign(byte bytesToSign[]) throws Exception {
return new PKCS7Signer().sign(CERTIFICATE_PATH,CERTIFICATE_PASS, bytesToSign);
}
/**
* Servlet handler , should listen on the callback URL (as in webServiceURL)
* #param requestPath
* #param req
* #param servletRequest
* #param servletResponse
* #throws Exception
*/
public static void main(String[] args) throws Exception {
Packager.createPackageFile("SafriNotifcation","");
}
}
PKCS7Signer.java which create your signature file.
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
public final class PKCS7Signer {
static {
try{
Security.addProvider(new BouncyCastleProvider());
}catch(Exception e){
e.printStackTrace();
}
}
private KeyStore getKeystore(String storeLocation, String storePasswd) throws Exception {
if (storeLocation == null) {
System.out.println("Could not find store file (.p12)");
return null;
}
// First load the keystore object by providing the p12 file path
KeyStore clientStore = KeyStore.getInstance("PKCS12");
// replace testPass with the p12 password/pin
clientStore.load(new FileInputStream(storeLocation), storePasswd.toCharArray());
return clientStore;
}
private X509CertificateHolder getCert(KeyStore keystore, String alias) throws Exception {
java.security.cert.Certificate c = keystore.getCertificate(alias);
return new X509CertificateHolder(c.getEncoded());
}
private PrivateKey getPrivateKey(KeyStore keystore, String alias, String storePasswd) throws Exception {
return (PrivateKey) keystore.getKey(alias, storePasswd.toCharArray());
}
public byte[] sign(String storeLocation, String storePasswd, byte[] dataToSign) throws Exception {
KeyStore clientStore = getKeystore(storeLocation, storePasswd);
if (clientStore == null) {
return null;
}
Enumeration aliases = clientStore.aliases();
String alias = "";
while (aliases.hasMoreElements()) {
alias = (String) aliases.nextElement();
if (clientStore.isKeyEntry(alias)) {
break;
}
}
CMSTypedData msg = new CMSProcessableByteArray(dataToSign); // Data to sign
X509CertificateHolder x509Certificate = getCert(clientStore, alias);
List certList = new ArrayList();
certList.add(x509Certificate); // Adding the X509 Certificate
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
// Initializing the the BC's Signer
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(
getPrivateKey(clientStore, alias, storePasswd));
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder()
.setProvider("BC").build()).build(sha1Signer, x509Certificate));
// adding the certificate
gen.addCertificates(certs);
// Getting the signed data
CMSSignedData sigData = gen.generate(msg, false);
return sigData.getEncoded();
}
}
please note used latest bouncy castle jars for create signature file
bcprov-jdk15on-157.jar
bcpkix-jdk15on-157.jar
Write your javascript on your client side page.it contains simple java script to get permissions.
///
// For safari
var domain="YOUR WEB PUSH ID";
function safariIniti() {
var pResult = window.safari.pushNotification.permission(domain);
if(pResult.permission === 'default') {
//request permission
requestPermissions();
} else if (pResult.permission === 'granted') {
console.log("Permission for " + domain + " is " + pResult.permission);
var token = pResult.deviceToken;
// Show subscription for debug
console.log('Subscription details:'+token);
} else if(pResult.permission === 'denied') {
console.log("Permission for " + domain + " is " + pResult.permission);
}
}
function getToken(){
// always start with a letter (for DOM friendlyness)
var idstr=String.fromCharCode(Math.floor((Math.random()*25)+65));
do {
// between numbers and characters (48 is 0 and 90 is Z (42-48 = 90)
var ascicode=Math.floor((Math.random()*42)+48);
if (ascicode<58 || ascicode>64){
// exclude all chars between : (58) and # (64)
idstr+=String.fromCharCode(ascicode);
}
} while (idstr.length<32);
return (idstr);
}
function requestPermissions() {
var tokenVal = getToken();
window.safari.pushNotification.requestPermission('WEb service url without end points',domain,{token:tokenVal},
function(subscription) {
console.log(subscription.permission);
console.log("PERMISSION ====>> "+subscription.permission);
if(subscription.permission === 'granted') {
//TODO
}
else if(subscription.permission === 'denied') {
// TODO:
}
});
}
Apple provides a php file that you can use to create your push package including the signature. https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NotificationProgrammingGuideForWebsites/CompanionFile.zip
Alternatively, you can use the push_package gem, https://github.com/SymmetricInfinity/push_package, which we developed when implementing safari push notifications for zeropush.com. More details are available at https://zeropush.com/blog/implementing-safari-push-notifications-in-osx-mavericks.
Follow this apple documentation and github repo, they include sufficient information required to create a safari push notifications.