Google OAuth, handle a revoked authorization - java

I've been using Google OAuth to let users authorize access to the Calendar Service for my Web Application. After a successful 3-legged auth flow, I was storing all user's credentials in a common file on the app Server. The next time the app needs to use the service, it will check if the credentials exist, and if yes, it will assume they are valid
code works like that
#Override
public void _authorize(String userId) throws IOException {
// Check if user has already authorised the service.
Credential credents = flow.loadCredential(userId);
// Checking if the given user is not authorized
if (credents == null) {
//Create credentials now. user will be redirected to authorise
try {
//Creating a LocalServer Receiver
// Getting the redirect URI
// Creating a new authorization URL
// Setting the redirect URI
// Building the authorization URL
// Receiving authorization code
// Exchanging it for an access token
// Storing the credentials for later access
credents = flow.createAndStoreCredential(response, id);
} finally {
// Releasing resources
}
} else {
// Assume the credentials are valid. so there's nothing left to do here, let's get that client
//Update: Nooooooot! the user might have revoked the authorization, so credents != null BUT they are invalid
//TODO: handle an Exception here, and manage the revoked credentials
}
// Setting up the calendar service client
client = new com.google.api.services.calendar.Calendar.Builder(httpTransport, jsonFactory, credents).setApplicationName(APPLICATION_NAME)
.build();
}
This works fine, as long as the user never changes his mind. But if the user decides to manually revoke the authorization using the Google Account security options, the com.google.api.services.calendar.Calendar retrieval will Fail.
My question is :
Is there a way to check if the credentials are still valid, before trying to use them ?
Else, I can only guess that the failure to get the client object, is the only way to have my portal realize that the credentials are no more valid ?
What should I do about the invalid/revoked credentials ? should I just call flow.createAndStoreCredential and they are going to be overwritten? Or do I have to delete the old ones first ? (how ?)

You can use the refreshToken() method for this. See example:
// Fetch credential using the GoogleAuthorizationCodeFlow
GoogleAuthorizationCodeFlow authorizationCodeFlow;
Credential credential = authorizationCodeFlow.loadCredential(userId);
if (credential != null) {
try {
// refresh the credential to see if the refresh token is still valid
credential.refreshToken();
System.out.println("Refreshed: expires in: " + credential.getExpiresInSeconds());
} catch (TokenResponseException e) {
// process exception here.
// This will catch the Exception.
// This Exception contains the HTTP status and reason etc.
// In case of a revoke, this will throw something like a 401 - "invalid_grant"
return;
}
} else {
// No credential yet known.
// Flow for creating a new credential here
}
EDIT
If you indeed have an invalid refresh token and you want to renew it, then you need to repeat the steps that you did in the first place to get the credentials. So:
genererate a new authorization URL
redirect the user to it
user accepts the consent screen
catch the authorization code from the redirect back to your app
request a new token from Google using the authorization code
create and store a new Credential using the response from Google
No need to delete the old credential. But if you want to explicitly do so, it is possible.
Something like:
// This userId is obviously the same as you used to create the credential
String userId = "john.doe";
authorizationCodeFlow.getDataStore().delete(userId);

You can use the endpoint https://www.googleapis.com/oauth2/v1/tokeninfo to determine if an OAuth2 token is still valid. More information is available in the OAuth2 guide.

Answer to the first question:
When using the Service object for retrieving calendar items from Google Calendar, the token are automatically verified. When they are invalid, they will be refreshed automatically, and stored in the datastore you provided to the flow.
this can also be done manually. A token is valid for 3600 seconds (one hour). When retrieving a token you get this value with the timestamp when it was issued. You could manually determine if a token is valid. If it is not valid call the following async method.
await credents.RefreshtokenAsync(CancellationToken.None);
This function gets you fresh tokens, and stores them in the datastore you provided.

You could check token with tokeninfo and if token is not valid:
- remove credential from datastore
- invoke new auth
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
UserService userService = UserServiceFactory.getUserService();
if (userService.isUserLoggedIn()) {
User user = userService.getCurrentUser();
log.info(String.format("LoggedUser: %s %s", user.getEmail(), user.getUserId()));
Credential credential = this.getCredential();
Tokeninfo tokenInfo = OAuth2Utils.getTokenInfo(credential, null);
if (tokenInfo != null)
log.info(String.format("Token expires in: %d", tokenInfo.getExpiresIn()));
else {
OAuth2Utils.deleteCredential(user.getUserId());
response.sendRedirect(request.getRequestURI()); // recall this servlet to require new user authorization
return;
}
}
public static Tokeninfo getTokenInfo(Credential credential, String accessToken) {
Oauth2 service = new Oauth2.Builder(new NetHttpTransport(), Constant.JSON_FACTORY, credential).setApplicationName(Constant.APP_NAME).build();
Tokeninfo tokenInfo = null;
try {
tokenInfo = service.tokeninfo().setAccessToken( accessToken == null ? credential.getAccessToken() : accessToken ).execute();
} catch (IOException e) {
log.warning("An error occurred: " + e);
}
return tokenInfo;
}

Related

Firebase: Failed to verify the signature of Firebase ID token

When I try to verify the Firebase jwt token in my Spring Boot backend application, I get the following error:
Failed to verify the signature of Firebase ID token. See
https://firebase.google.com/docs/auth/admin/verify-id-tokens for
details on how to retrieve an ID token.
In the client (Flutter) I log the jwt as follows:
GoogleSignInAccount googleSignInAccount = await _googleSignIn.signIn();
GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication;
AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
UserCredential authResult = await _auth.signInWithCredential(credential);
_user = authResult.user;
logger.i(await _user.getIdToken()); // Print jwt
I send the jwt that gets logged to my backend through the Authorization header as a bearer token.
Using Spring security (it doesn't matter), I just perform the following check:
FirebaseToken decoded = FirebaseAuth.getInstance().verifyIdToken(token);
My firebase app init config is pretty standard (env variable pointing to config.json is set):
#Primary
#Bean
public void firebaseInit() throws IOException {
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.getApplicationDefault())
.build();
if (FirebaseApp.getApps().isEmpty()) {
FirebaseApp.initializeApp(options);
}
}
After debugging, following method throws in class RSASignature (package package sun.security.rsa):
#Override
protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
if (publicKey == null) {
throw new SignatureException("Missing public key");
}
try {
if (sigBytes.length != RSACore.getByteLength(publicKey)) {
throw new SignatureException("Signature length not correct: got " +
sigBytes.length + " but was expecting " +
RSACore.getByteLength(publicKey));
}
sigBytes length is 113, whereas it expects to be 256.
Perhaps I'm doing something wrong though...
My God... the logger that I used in dart decided to just cap the jwt string so the jwt was incomplete.
Now I get a message 'forbidden' happy joy. But the previous error has been resolved.
Edit 'Forbidden' was consequence of a minor Spring Boot issue (adding roles to authorities).
It now works as expected.

Enabling OAuth2 with Feign for scheduled cross-service tasks

I'm trying to solve a puzzle with enabling OAuth2-based authentication for my Feign client that is used for cross-service communication.
In normal cases, when a user pushes a request through API, I'm able to take all authentication details needed from the Spring's SecurityContextHolder (as it normally does its job and fills all the details objects) and enhance Feign's Request as follows:
public class FeignAccessTokenRelyInterceptor implements RequestInterceptor {
private static final Logger log = LoggerFactory.getLogger(FeignAccessTokenRelyInterceptor.class);
#Override
public void apply(RequestTemplate template) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
String tokenValue = null;
if (auth.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) auth.getDetails();
tokenValue = details.getTokenValue();
}
if (tokenValue == null) {
log.warn("Current token value is null");
return;
}
template.header("Authorization", "Bearer " + tokenValue);
}
}
}
However, when it comes to scheduled calls that are triggered inside the system, the SecurityContext is obviously empty. I'm filling it with UserInfoTokenServices by manually requesting the access token by client credentials flow beforehand and loading user details:
OAuth2Authentication authentication = userInfoTokenServices.loadAuthentication(accessToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
But such construction doesn't fill OAuth2Authentication.details, on which I rely to get an access token. I tried extending OAuth2AuthenticationDetails, but the only constructor requires HttpServletRequest which is hard to get inside a scheduled task, and making a dummy instance of it feels like a bad choice.
So far, I see the only adequate option to make a separate custom implementation of details holder and pass it to OAuth2Authentication along with the access token I have. And then pick it up in FeignAccessTokenRelyInterceptor.
The question
Maybe there are some other options where I can store my access token in the security context and reliably get it from there, in order not to produce new custom classes?
Will be glad for any help.
Some related links I've studied:
How to get custom user info from OAuth2 authorization server /user endpoint
Spring Boot / Spring Cloud / Spring Security: How to correctly obtain an OAuth2 Access Token in a scheduled task
Spring #FeignClient , OAuth2 and #Scheduled not working
How can I authenticate a system user for scheduled processes in Spring?
For the history, hope that'd help someone like me struggling with that.
I didn't find a better way than the initial one and made a custom InternalOAuth2Details to hold a token value obtained from Spring's OAuth services. Then, in the FeignAccessTokenRelyInterceptor I simply check if current details are InternalOAuth2Details and try to get a token value from it, as follows:
#Override
public void apply(RequestTemplate template) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
String tokenValue = null;
if (auth.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) auth.getDetails();
tokenValue = details.getTokenValue();
} else if (auth.getDetails() instanceof InternalOAuth2Details) {
InternalOAuth2Details details = (InternalOAuth2Details) auth.getDetails();
tokenValue = details.getTokenValue();
}
if (tokenValue == null) {
log.warn("Current token value is null");
return;
}
template.header("Authorization", "Bearer " + tokenValue);
}
}
I bet it isn't the best solution, but it seems to work quite stable as of now.

Java SPNEGO Authentication & Kerberos Constrained Delegation (KCD) to backend service

I have a Java web application which do SPNEGO authentication of clients in a Windows Active Directory environment.
To authenticate the user we use code from the good old SPNEGO SourceForge project.
String encodedAuthToken = (String) credentials;
LOG.debug("Encoded auth token: " + encodedAuthToken);
byte[] authToken = B64Code.decode(encodedAuthToken);
GSSManager manager = GSSManager.getInstance();
try {
Oid krb5Oid = new Oid("1.3.6.1.5.5.2");
GSSName gssName = manager.createName(_targetName, null);
GSSCredential serverCreds = manager.createCredential(gssName, GSSCredential.INDEFINITE_LIFETIME, krb5Oid, GSSCredential.INITIATE_AND_ACCEPT);
GSSContext gContext = manager.createContext(serverCreds);
if (gContext != null) {
while (!gContext.isEstablished()) {
authToken = gContext.acceptSecContext(authToken, 0, authToken.length);
}
if (gContext.isEstablished()) {
// Login succeeded!
String clientName = gContext.getSrcName().toString();
}
}
}
The authentication works good but we also have a requirement to delegate the user credentials to a back-end service (Exchange EWS), using constrained delegation.
When configuring this in our AD it looks like a small difference, but it's not. See:
AD delegation settings
The difference is described here: msdn.microsoft.com/en-us/library/cc246080.aspx?f=255&MSPPError=-2147217396
With unconstrained delegation we could simply use the available delegated credentials when we call the back-end service and it would all be good:
GSSCredential delegatedCreds = gContext.getDelegCred()
SpnegoHttpURLConnection conn = new SpnegoHttpURLConnection(clientCreds);
With constrained delegation we have no access to the users TGT and it seems we need to use the MS-SFU (S4U2proxy) Kerberos extension which Java 8 is suppose to support.
The only example I could find is this one: https://github.com/ymartin59/java-kerberos-sfudemo (thanks Yves Martin for that!)
Now to my problem... After my authentication I basically end up with the username of the authenticated user (see "clientName" in code above).
Do we really need to use the S4U2self mechanism to impersonate the user here?
The client just sent us it's Kerberos Service Ticket (wrapped in the SPNEGO token I can't decode).
Ideally we should be able to use that service ticket and my own service's TGT to authenticate the user (using the S4U2proxy mechanism)?
But I do not understand how.
So now I'm wondering if it's possible to tie together our SPNEGO authentication with S4U2proxy delegation?
Many thanks for any input on this.
I've actually been doing something like this recently but am using spring security kerberos. I put an example on github here. The key thing that I found that I needed set up to use constrained delegation like you want it and S4U2Proxy was to make sure (if you're using Oracle/OpenJDK) you set isInitiator=true in your JAAS Config so that when getDelegCred is called you get back a Krb5ProxyCredential. See comment here. With that credential, you can use it to create service ticket tokens on the Users behalf for the services you are constrained to use in the normal fashion, like this.
I've done a lot of investigation on Kerberos constrained delegation, and finally I've figured out the correct way of doing it using Java.
Settings on Domain Controller
1) No Delegation: Do not trust this account for delegation
You (service user) can not get delegated credentials of the user. It means you can not perform any task on end user's behalf.
At the most you can do is to accept the incoming ticket from the user(usually browser) and get it verified by passing it to KDC. In response, KDC will tell you for which user(or principal) this ticket is issued to, but no credentials will be passed.
2) Unconstrained Delegation: Trust this account for delegation to any service (Kerberos only)
With this option, you (service user) get the delegated credentials of the user. Moreover, what you get is a TGT of the user. Using this TGT, you can request TGS (service ticket) on user's behalf for any service.
3) Trust this account for delegation to specified services (Kerberos only)
Here, you specify the services to which you can use the delegated credentials. It means when this option is enabled, you get the delegated credentials, however, you are allowed to use them only to get end user's TGS for the specified services.
Another important point is, you must have end user's TGS (end user's TGS for your web app). Then using this TGS, you can request KDC the end user's TGS for another service.
4) Trust this account for delegation to specified services (Any Protocol)
This is also known as protocol transition. In this option also, you need to specify the services for which you can request the TGS to KDC on user's behalf.
You (service user) are allowed to 'impersonate' the end user, without having any kind of ticket from the end user. You can impersonate any user, and get TGS for the specified services.
This option is useful for backgroung processes or schedulars where end user interaction is not possible.
Java Code Samples
1) Getting Delegated Credentials (useful in option 2 and 3 stated above)
// ---------------------------------
// step 1: Login using service user credentials and get its TGT
// ---------------------------------
Subject subject = new Subject();
Krb5LoginModule krb5LoginModule = new Krb5LoginModule();
Map<String,String> optionMap = new HashMap<String,String>();
optionMap.put("keyTab", "c:\\ticket\\sapuser.keytab");
optionMap.put("principal", "HTTP/TEST"); // SPN you mapped to the service user while creating the keytab file
optionMap.put("doNotPrompt", "true");
optionMap.put("refreshKrb5Config", "true");
optionMap.put("useTicketCache", "true");
optionMap.put("renewTGT", "true");
optionMap.put("useKeyTab", "true");
optionMap.put("storeKey", "true");
optionMap.put("isInitiator", "true"); // needed for delegation
optionMap.put("debug", "true"); // trace will be printed on console
krb5LoginModule.initialize(subject, null, new HashMap<String,String>(), optionMap);
krb5LoginModule.login();
krb5LoginModule.commit();
// ---------------------------------
// Step 2: Use login context of this service user, accept the kerberos token (TGS) coming from end user
// ---------------------------------
public GSSCredential validateTicket(byte[] token) {
try {
return Subject.doAs(this.serviceSubject, new KerberosValidateAction(token));
}
catch (PrivilegedActionException e) {
throw new BadCredentialsException("Kerberos validation not successful", e);
}
}
private class KerberosValidateAction implements PrivilegedExceptionAction<GSSCredential> {
byte[] kerberosTicket;
public KerberosValidateAction(byte[] kerberosTicket) {
this.kerberosTicket = kerberosTicket;
}
#Override
public GSSCredential run() throws Exception {
byte[] responseToken = new byte[0];
GSSName gssName = null;
GSSContext context = GSSManager.getInstance().createContext((GSSCredential) null);
while (!context.isEstablished()) {
responseToken = context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length);
gssName = context.getSrcName();
if (gssName == null) {
throw new BadCredentialsException("GSSContext name of the context initiator is null");
}
}
//check if the credentials can be delegated
if (!context.getCredDelegState()) {
SecurityLogger.getLogger().error("Credentials can not be delegated. Please make sure that delegation is enabled for the service user. This may cause failures while creating Kerberized application.");
return null;
}
// only accepts the delegated credentials from the calling peer
GSSCredential clientCred = context.getDelegCred(); // in case of Unconstrained Delegation, you get the end user's TGT, otherwise TGS only
return clientCred;
}
}
// ---------------------------------
// Step 3: Initiate TGS request for another service using delegated credentials obtained in previous step
// ---------------------------------
private Object getServiceTicket(GSSCredential clientCred) throws PrivilegedActionException {
Object o = Subject.doAs(new Subject(), (PrivilegedExceptionAction<Object>) () -> {
GSSManager manager = GSSManager.getInstance();
Oid SPNEGO_OID = new Oid("1.3.6.1.5.5.2");
Oid KRB5_PRINCIPAL_OID = new Oid("1.2.840.113554.1.2.2.1");
GSSName servicePrincipal = manager.createName("HTTP/TEST", KRB5_PRINCIPAL_OID); // service to which the service user is allowed to delegate credentials
ExtendedGSSContext extendedContext = (ExtendedGSSContext) manager.createContext(servicePrincipal, SPNEGO_OID, clientCred, GSSContext.DEFAULT_LIFETIME);
extendedContext.requestCredDeleg(true);
byte[] token = new byte[0];
token = extendedContext.initSecContext(token, 0, token.length); // this token is the end user's TGS for "HTTP/TEST" service, you can pass this to the actual HTTP/TEST service endpoint in "Authorization" header.
return token;
});
return o;
}
2) Getting Impersonated Credentials (useful in option 4 stated above)
Initial steps are similar as mentioned inside step 1 above. You need to login using service user credentials. There is small change in 'run' method, which is given below:
#Override
public GSSCredential run() throws Exception {
GSSName gssName = null;
GSSManager manager = GSSManager.getInstance();
GSSCredential serviceCredentials = manager.createCredential(GSSCredential.INITIATE_ONLY);
GSSName other = manager.createName("bhushan", GSSName.NT_USER_NAME, kerberosOid); // any existing user
GSSCredential impersonatedCredentials = ((ExtendedGSSCredential) serviceCredentials).impersonate(other);
return impersonatedCredentials;
}
}
You can see that we don't need user's TGS in this case.Getting TGS on user's behalf for other service, is same as mentioned in step 3 given in above code. Just pass these impersonatedCredentials instead of delegatedCredentials.
I hope this will be helpful.
Thanks,
Bhushan

How to make a request for an access token using the refresh token with Google OAuth 2.0

i think the context of my question is clear enough, but i would like to specifiy that i'm using a OAuth for Web Application, so I used this Example provided by Google: OAuth 2.0 for WebApplication
I understood that i have to create (for example in the same class (extended by HttpServlet)) a doGet method (obviously in Override) to obtain the correct flow to authorize the user with own scopes, so something like this:
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//This is a class to obtain the Current Google User's ID
UserService userService = UserServiceFactory.getUserService();
String userId = userService.getCurrentUser().getUserId();
String url = getAuthorizationUrl(userId);
if(req.getParameter("code")!=null){
try {
String authorizationCode = req.getParameter("code");
getCredentials(authorizationCode);
} catch (CodeExchangeException | NoRefreshTokenException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//the next page (my homePage)
resp.sendRedirect("/home");
}
else{
resp.sendRedirect(url);
}
}
This successfully work, if you look at the provided method 'getCredential(authorizationCode)' it shows 2 method: storeCredential and getStoredCredential.
The first method works perfectly and i can see the refreshToken and accessToken strings in my Datastore indeed.
The problem is to build the getStoredCredential method, because it returns a Credential object and i don't know to write it. I show you the beginning of the method but i don't know how to carry on.
static Credential getStoredCredentials(String googlePlusUserId) {
// TODO: Implement this method to work with your database. Instantiate a new
// Credential instance with stored accessToken and refreshToken.
//These methods of my Class correctly get the tokens (already debugged)
String refreshToken = UserManager.getStoredRefreshToken(googlePlusUserId);
String accessToken = UserManager.getStoredAccessToken(googlePlusUserId);
//How can i continue here? :-( to return Credential with correct parameters?
//......
//......
//......
//......
}
Does this method works forever?
Because in Google OAuth Playgroung there is a checkbox that says: "Auto-refresh the token before it expires" (i think in 3600 seconds). So, i don't think that completing my getStoredCredential get my App working correctly...I think that is only useful for get always the last token...So in this case i can never obtain a new accessToken using the Stored RefreshToken.
Does anyone can help me please? Maybe with a simple method that refresh the old AccessToken.

Using Oauth and Gdata to build a simple application in Java

I am trying to create a simple app on the app engine where users log
in through their Google account, and then it adds an event to their
calendar.
And I am using Java along with Eclipse for this. I have found a simple
code online:
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// Create an instance of GoogleOAuthParameters
GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);
oauthParameters.setScope("http://docs.google.com/feeds/");
GoogleOAuthHelper oauthHelper = new GoogleOAuthHelper(
new OAuthHmacSha1Signer());
// Remember the token secret that we stashed? Let's get it back
// now. We need to add it to oauthParameters
String oauthTokenSecret = (String) req.getSession().getAttribute(
"oauthTokenSecret");
oauthParameters.setOAuthTokenSecret(oauthTokenSecret);
// The query string should contain the oauth token, so we can just
// pass the query string to our helper object to correctly
// parse and add the parameters to our instance of oauthParameters
oauthHelper.getOAuthParametersFromCallback(req.getQueryString(),
oauthParameters);
try {
// Now that we have all the OAuth parameters we need, we can
// generate an access token and access token secret. These
// are the values we want to keep around, as they are
// valid for all API calls in the future until a user revokes
// our access.
String accessToken = oauthHelper.getAccessToken(oauthParameters);
String accessTokenSecret = oauthParameters.getOAuthTokenSecret();
// In a real application, we want to redirect the user to a new
// servlet that makes API calls. For the safe of clarity and simplicity,
// we'll just reuse this servlet for making API calls.
oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);
// This is interesting: we set the OAuth token and the token secret
// to the values extracted by oauthHelper earlier. These values are
// already in scope in this example code, but they can be populated
// from reading from the datastore or some other persistence mechanism.
oauthParameters.setOAuthToken(accessToken);
oauthParameters.setOAuthTokenSecret(accessTokenSecret);
oauthParameters.setOAuthCallback("http://www.facebook.com");
oauthHelper.getUnauthorizedRequestToken(oauthParameters);
// Create an instance of the DocsService to make API calls
DocsService client = new DocsService("Malware Inc.");
// Use our newly built oauthParameters
client.setOAuthCredentials(oauthParameters, new OAuthHmacSha1Signer());
URL feedUrl = new URL("https://docs.google.com/feeds/default/private/full");
DocumentListFeed resultFeed = client.getFeed(feedUrl,
DocumentListFeed.class);
for (DocumentListEntry entry : resultFeed.getEntries()) {
resp.getWriter().println(entry.getTitle().getPlainText());
}
} catch (OAuthException e) {
// Something went wrong. Usually, you'll end up here if we have invalid
// oauth tokens
resp.getWriter().println("Here is the problem");
//Server shows 500 problem
} catch (ServiceException e) {
// Handle this exception
}
}
I have registered my application and added the KEY and Secret above
the function, but when I deploy it to the app engine it gives a 500
server error.
Could someone post a simple java program that uses gdata and oauth to
log in a Google user and print the contacts on the screen?
Thanks.
-Manoj
I was facing the same problem, and it took me a while to figure it out.
Actually, the problem is that your are missing some parts in the OAuth authorization process.
As you may know, it a 3-legged process:
Get an unauthorized request token
Authorize the request token
Exchange the authorized request token for an access token and make calls to Google Data with it.
In your case, you are doing step 3 directly.
So before you can call the servlet you described above, and effectively retrieve user's Google Data,
the user must have grant access to your application, by browsing to an authorization URL from his web browser.
You need a first servlet , for example accessible at http://yourapp.com/RequestAccess
public void doGet(HttpServletRequest req, HttpServletResponse resp) {
GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(YOUR_CONSUMER_KEY);
oauthParameters.setOAuthConsumerSecret(YOUR_CONSUMER_SECRET);
OAuthHmacSha1Signer signer = new OAuthHmacSha1Signer();
GoogleOAuthHelper oauthHelper = new GoogleOAuthHelper(signer);
oauthParameters.setScope(FEED_SCOPE);
try {
oauthHelper.getUnauthorizedRequestToken(oauthParameters);
//GET THE UNAUTHORIZED TOKENS
String oauthRequestToken = oauthParameters.getOAuthToken();
String oauthTokenSecret = oauthParameters.getOAuthTokenSecret();
//SAVE THEM SOMEWEHERE (FOR EXAMPLE IN THE SESSION LIKE YOU DID)
// ....
//GET THE AUHTORIZATION URL
String authorizationURL= oauthHelper.createUserAuthorizationUrl(oauthParameters);
// YOU NOW HAVE THE AUHTORIZATION URL, SEND IT BACK TO THE USER SOMEHOW
// ( FOR EXAMPLE BY REDIRECTING THE REQUEST TO THAT URL)
// ...
} catch (OAuthException e1) {
LOGGER.error("error while getting unauthorized request token '{}' ", e1);
}
}
Once the user has navigate to that URL, and grant acces, you can now call your second servlet and it should work.
More info can be found on Google OAuth page here
Hope it helps!

Categories

Resources