Closed angelique360 closed 4 years ago
I am also getting the same issue for facebook login
Hello friend, I solve my problem by doing facebook login by hand, example:
def code = params.code
def query = "https://graph.facebook.com/v2.8/oauth/access_token?client_id=********&redirect_uri=*****&client_secret=****&code=${params.code}
//call http
def response = ApiClient.get(query)
if(response?.code == HttpStatus.SC_OK){
def access_token = JSON.parse(response.message)?.access_token
def response2 = ApiClient.get("https://graph.facebook.com/v2.8/me?fields=id,name,first_name,middle_name,last_name,email,picture.height(200).width(200)&access_token="+access_token)
if(response2?.code == HttpStatus.SC_OK){
def profile = JSON.parse(response2.message)
def uid = profile?.id ?: null
def first_name = profile?.first_name ?: null
def last_name = profile?.last_name ?: null
def email = profile?.email ?: null
def pictureUrl = profile?.picture ? profile?.picture?.data?.url : null
def userInstance = User.findByUsername(uid)
def role = null
if (!userInstance) {
//Insert user
}
springSecurityService.reauthenticate(userInstance.username)
//use and import service userDetailsService
def userDetail = userDetailsService.loadUserByUsername(userInstance.username)
//use and import service tokenGenerator
def tokenValue = tokenGenerator.generateAccessToken(userDetail)
//return or redirec
return tokenValue
@angelique360 Thanks a lot for sharing your solution. I am going to try it.
I have the same problem...
package org.scribe.extractors;
import java.util.regex.*;
import org.scribe.exceptions.*;
import org.scribe.model.*;
import org.scribe.utils.*;
public class TokenExtractor20Impl implements AccessTokenExtractor
{
private static final String TOKEN_REGEX = "access_token=([^&]+)";
private static final String EMPTY_SECRET = "";
/**
* {@inheritDoc}
*/
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(TOKEN_REGEX).matcher(response);
if (matcher.find())
{
String token = OAuthEncoder.decode(matcher.group(1));
return new Token(token, EMPTY_SECRET, response);
}
else
{
throw new OAuthException("Response body is incorrect. Can't extract a token from this: '" + response + "'", null);
}
}
}
This class use TOKEN_REGEX for extract the token but now the respose is a Json.
https://developers.facebook.com/docs/apps/changelog
[Oauth Access Token] Format - The response format of https://www.facebook.com/v2.3/oauth/access_token returned when you exchange a code for an access_token now return valid JSON instead of being URL encoded. The new format of this response is {"access_token": {TOKEN}, "token_type":{TYPE}, "expires_in":{TIME}}. We made this update to be compliant with section 5.1 of RFC 6749.
I had the same problem. So basically response from facebook is now coming as JSON.
The solution I have derived is to write our own custom facebook Api like below
Create the class FacebookCustomApi
under /src/java/
public class FacebookCustomApi extends DefaultApi20{
private static final String AUTHORIZE_URL = "https://www.facebook.com/dialog/oauth?response_type=code&client_id=%s&redirect_uri=%s";
private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=%s";
@Override
public String getAccessTokenEndpoint() {
return "https://graph.facebook.com/oauth/access_token";
}
@Override
public AccessTokenExtractor getAccessTokenExtractor() {
return new AccessTokenExtractor() {
@Override
public Token extract(String response) {
Preconditions.checkEmptyString(response, "Response body is incorrect. Can't extract a token from an empty string");
try {
//Here is the real deal, Just create the JSON from response and get the access token from it
JSONObject json = new JSONObject(response);
String token = json.getString("access_token");
return new Token(token, "", response);
} catch (Exception e){
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()) {
return String.format(SCOPED_AUTHORIZE_URL, config.getApiKey(),
OAuthEncoder.encode(config.getCallback()),
OAuthEncoder.encode(config.getScope()));
} else {
return String.format(AUTHORIZE_URL, config.getApiKey(),
OAuthEncoder.encode(config.getCallback()));
}
}
@Override
public Verb getAccessTokenVerb() {
return Verb.POST;
}
@Override
public OAuthService createService(OAuthConfig config) {
return new FacebookOAuth2Service(this, config);
}
private class FacebookOAuth2Service extends OAuth20ServiceImpl {
private static final String GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code";
private static final String GRANT_TYPE = "grant_type";
private DefaultApi20 api;
private OAuthConfig config;
public FacebookOAuth2Service(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());
request.addBodyParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret());
request.addBodyParameter(OAuthConstants.CODE, verifier.getValue());
request.addBodyParameter(OAuthConstants.REDIRECT_URI, config.getCallback());
request.addBodyParameter(GRANT_TYPE, GRANT_TYPE_AUTHORIZATION_CODE);
break;
case GET:
default:
request.addQuerystringParameter(OAuthConstants.CLIENT_ID, config.getApiKey());
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());
}
}
}
And use it in our oauth config as below
//for facebook authentication
oauth {
providers {
facebook {
api = FacebookCustomApi
@Prakash-Thete Your solution doesn't seem to be working for me, i am unable to over-ride the API? any guesses why?
Instead of overriding whole of FacebookApi class I searched for the way to replace AccessTokenExtractor getAccessTokenExtractor()
method which by default uses org.scribe.extractors.TokenExtractor20Impl
, I wanted to replace it with org.scribe.extractors.JsonTokenExtractor
It was a bit odd as class that needs to have method overridden ExtendedFacebookApi
is declared final
so had to rewrite it:
OverridenFacebookApi.groovy
package foobar
import org.scribe.builder.api.StateApi20
import org.scribe.extractors.AccessTokenExtractor
import org.scribe.extractors.JsonTokenExtractor
import org.scribe.model.OAuthConfig
import org.scribe.utils.OAuthEncoder
import org.scribe.utils.Preconditions
class OverridenFacebookApi extends StateApi20 {
private static final String AUTHORIZE_URL_WITH_STATE = "https://www.facebook.com/dialog/oauth?display=popup&client_id=%s&redirect_uri=%s&state=%s";
private static final String SCOPED_AUTHORIZE_URL_WITH_STATE = AUTHORIZE_URL_WITH_STATE + "&scope=%s";
@Override
AccessTokenExtractor getAccessTokenExtractor() {
return new JsonTokenExtractor()
}
@Override
String getAccessTokenEndpoint() {
return "https://graph.facebook.com/oauth/access_token";
}
@Override
String getAuthorizationUrl(final OAuthConfig config,
final String state) {
Preconditions
.checkEmptyString(config.getCallback(),
"Must provide a valid url as callback. Facebook does not support OOB");
// Append scope if present
if (config.hasScope()) {
return String.format(SCOPED_AUTHORIZE_URL_WITH_STATE,
config.getApiKey(),
OAuthEncoder.encode(config.getCallback()),
OAuthEncoder.encode(state),
OAuthEncoder.encode(config.getScope()));
} else {
return String.format(AUTHORIZE_URL_WITH_STATE, config.getApiKey(),
OAuthEncoder.encode(config.getCallback()),
OAuthEncoder.encode(state));
}
}
}
Then had to make sure FacebookClient
is using it, so I've extended it and changed initialisation to use the class above.
OverridenFacebookClient.groovy
package foobar
import groovy.transform.InheritConstructors
import org.apache.commons.lang3.StringUtils
import org.pac4j.core.util.CommonHelper
import org.pac4j.oauth.client.FacebookClient
import org.scribe.model.OAuthConfig
import org.scribe.model.SignatureType
import org.scribe.oauth.StateOAuth20ServiceImpl
@InheritConstructors
class OverridenFacebookClient extends FacebookClient {
@Override
protected void internalInit() {
super.internalInit();
CommonHelper.assertNotBlank("fields", this.fields);
this.api20 = new OverridenFacebookApi();
if(StringUtils.isNotBlank(this.scope)) {
this.service = new StateOAuth20ServiceImpl(this.api20, new OAuthConfig(this.key, this.secret, this.callbackUrl, SignatureType.Header, this.scope, (OutputStream)null), this.connectTimeout, this.readTimeout, this.proxyHost, this.proxyPort);
} else {
this.service = new StateOAuth20ServiceImpl(this.api20, new OAuthConfig(this.key, this.secret, this.callbackUrl, SignatureType.Header, (String)null, (OutputStream)null), this.connectTimeout, this.readTimeout, this.proxyHost, this.proxyPort);
}
}
}
Finally had to reference it in Config.groovy
import foobar.OverridenFacebookClient
grails.plugin.springsecurity.rest.oauth.facebook.client = OverridenFacebookClient
Awesome workaround @ivarprudnikov !
Just a fix. On OverridenFacebookClient.internalInit you miss the context param. It should be:
@Override
protected void internalInit(final WebContext context) {
...
}
After upgrading pac4j (#416), the Facebook API supported now is 2.11
Hi, The plugin throws the following error when trying to sign in with facebook.
error:500, message:org.scribe.exceptions.OAuthException: Response body is incorrect. Can't extract a token from this: '{"access_token":"EAAOWKGC6MDcBAB9ZAka1zEc1","token_type":"bearer"}', error_description:org.scribe.exceptions.OAuthException: Response body is incorrect. Can't extract a token from this: '{"access_token":"EAAOWKGC6M","token_type":"bearer"}', error_code:OAuthException