I'm having trouble continuing an OAuth session using a token obtained on an iOS client from a back-service. Specifically it looks to be a permission problem:
iOS Client Obtains Access Token (ObjC / FB iOS SDK v3.24)
Session established with the following permissions:
[FBSession openActiveSessionWithReadPermissions:#[
#"email",
#"user_about_me",
#"user_friends",
#"user_birthday",
#"public_profile" . . .
On completion . . .
FBSession *session = [FBSession activeSession];
NSString *accessToken = [session.accessTokenData accessToken];
Access Token Sent to Backend (which is Spring Boot + Kotlin)
A Spring FacebookTemplate is instantiated using the token obtained above, as follows:
#Test fun testFacebookTemplate()
{
val facebook = FacebookTemplate("$$TOKEN_FROM_FACEBOOK_IOS_SDK$$")
//Raises exception . .
val profile = facebook.userOperations().userProfile
println("Profile: " + profile)
}
The OAuth session established on the iOS client is continued from the backend successfully, and eg, a Facebook friend list can be returned. However, attempting to retrieve the profile, as shown above raises an error:
Error from Facebook: {"error":{"message":"(#3) Application does not have the capability to make this API call." , "type":"OAuthException","code":3,"fbtrace_id":"B4C+eS3n2PW"}}
DEBUG o.s.s.f.a.impl.FacebookErrorHandler - Facebook error:
DEBUG o.s.s.f.a.impl.FacebookErrorHandler - CODE : 3
DEBUG o.s.s.f.a.impl.FacebookErrorHandler - TYPE : OAuthException
Question:
Which permission is missing to return the User object. This does not appear to be documented in Spring's FacebookTemplate
Is this requested during OAuth authentication/authorization ( in my case with the FB iOS SDK) or via the developer console? This is unclear to me because both the openActiveSessionWithPermissions and the definition of the application in Facebook's web console contain references to these permissions.
It appears as though Spring's FacebookTemplate v2.0.2.RELEASE has some permission related error when invoking the request for user profile against the Facebook Graph API v2.3
As a work-around use:
val profile = facebook.fetchObject("me", User::class.java,
"id", "first_name", "last_name", "email");
After facebook API change, field "first_name" was replaced by field : "name"
public FacebookUser getFacebookUserData() {
Facebook facebook = new FacebookTemplate(accessToken);
String[] fields = {"id", "name", "email"};
FacebookUser user = facebook.fetchObject("me", FacebookUser.class, fields);
return user;
}
where FacebookUser is :
public class FacebookUser {
String id;
String name;
String email;
public FacebookUser(){ }
public FacebookUser(String id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
Related
I am a beginner with communicating with APIs and I am currently working with the Plaid API in order to retrieve transaction data for an android application. Currently, I have successfully integrated with the Plaid Link and can obtain public tokens through the app, however I am now having trouble exchanging the public token for the access token. I obtain the public token from here:
private ActivityResultLauncher<LinkTokenConfiguration> linkAccountToPlaid = registerForActivityResult(
new OpenPlaidLink(),
result -> {
if (result instanceof LinkSuccess) {
//Exchange public token for persistent access token
I believe the following code on the server is used to exchange the public token:
app.post('/api/set_access_token', function (request, response, next) {
PUBLIC_TOKEN = request.body.public_token;
Promise.resolve()
.then(async function () {
const tokenResponse = await client.itemPublicTokenExchange({
public_token: PUBLIC_TOKEN,
});
prettyPrintResponse(tokenResponse);
ACCESS_TOKEN = tokenResponse.data.access_token;
ITEM_ID = tokenResponse.data.item_id;
if (PLAID_PRODUCTS.includes('transfer')) {
TRANSFER_ID = await authorizeAndCreateTransfer(ACCESS_TOKEN);
}
response.json({
access_token: ACCESS_TOKEN,
item_id: ITEM_ID,
error: null,
});
})
.catch(next);
});
I have the quickstart server running, my question is, how do I then make the call to the server to exchange the public token for the access token? or in other words, how do I make a post call, containing the public token to the server to receive the access token
Response from the Plaid Android team:
So in our Android quickstart sample on GitHub, we already have a Retrofit API set up to retrieve the link_token.
What you would want to do is to add something like
#POST("/api/get_transactions")
fun getTransactions(publicToken: PublicToken): Single<Transactions>
data class PublicToken(#SerializedName("public_token") publicToken: String)
data class Transactions( #SerializedName("transactions") val transactions: List<Transaction>
)
data class Transaction( #SerializedName("amount") val amount: Float,
#SerializedName("category") val category: String,
)
I am trying to upgrade an application (it should fetch emails from a mailbox every few minutes) from Microsoft EWS deprecated API to the new Graph API, but I am facing some issues.
This is my class for the connector :
public class O365graphApiConnector {
private final GraphServiceClient<Request> graphClient;
public O365graphApiConnector(String clientId, String username, String password) {
final UsernamePasswordCredential usernamePasswordCredential =
new UsernamePasswordCredentialBuilder()
.clientId(clientId)
.username(username)
.password(password)
.build();
final TokenCredentialAuthProvider tokenCredentialAuthProvider =
new TokenCredentialAuthProvider(usernamePasswordCredential);
graphClient=GraphServiceClient.builder()
.authenticationProvider(tokenCredentialAuthProvider)
.buildClient();
}
public User getUserProfile() {
return graphClient.me().buildRequest().get();
}
public MessageCollectionPage getOutlookEmails() {
return graphClient.me().messages().buildRequest().get();
}
}
I am using com.azure:azure-identity:1.4.2 and com.microsoft.graph:microsoft-graph:5.8.0.
I build the connector, passing the clientId, username and password. I am able to call getUserProfile , and I am getting something, so the authentication "works".
However, I get a 404 when calling getOutlookEmails :
SEVERE: Throwable detail:
com.microsoft.graph.http.GraphServiceException: Error code:
ResourceNotFound Error message: Resource could not be discovered.
GET https://graph.microsoft.com/v1.0/me/messages SdkVersion :
graph-java/v5.8.0
404 : Not Found [...]
When I run this in debug mode and intercept the token, it seems to be OK though : I have a bunch of rights that my admin has given to the applicative account :
"scp": "EWS.AccessAsUser.All Mail.Read Mail.Read.Shared Mail.ReadBasic Mail.ReadWrite
Mail.ReadWrite.Shared Mail.Send Mail.Send.Shared MailboxSettings.ReadWrite User.Read User.Read.All User.ReadWrite profile openid email"
This is part of what we see on the admin side (more rights were added after the screenshot was taken) :
My understanding is that this should be enough to get access to the emails of the given mailbox programmatically, but apparently, it's not.
Any idea of what I am missing ?
actually, the technical "user" I am using didn't really have a mailbox (despite the user name being an email address.. that confused me).
It had been given the permissions on the given mailbox I am interested in though, so the fix is simply to select the mailbox/user before retrieving the messages :
public MessageCollectionPage getOutlookEmailsFor(String mailbox) {
return graphClient.users(mailbox).messages().buildRequest().get();
}
I need to retrieve the roles associated to user, but I am working with wildfly, I have installed all jar keycloak in wildfly and my Java project, but can I retrieve this list by Java adapter?
Other options is call the rest api like any api by get, post, put, etc. But my first options was Adapters.
I make the authentication by adapters, but I do not find any way to retrieve roles, clients, realms, etc.
I am wrong or the adapter is just to authentications?
Anyone have a good example?
Set the option use-resource-role-mappings : true in keycloak.json
and you should be able to get roles in servlet as follows
KeycloakPrincipal principal = (KeycloakPrincipal)request.getUserPrincipal();
principal.getKeycloakSecurityContext().getToken().getResourceAccess("testclient").getRoles();
You can also get KeycloakPrincipal from context like this
Subject subject = (Subject) PolicyContext.getContext("javax.security.auth.Subject.container");
Set<KeycloakPrincipal> principals = subject.getPrincipals(KeycloakPrincipal.class);
and then get the roles
Thanks, here other way: (retrieve one role by name)
Keycloak keycloak = Keycloak.getInstance("http://localhost/auth", "realm-name", "client-name", authorization);
RoleRepresentation role = keycloak.realm("realm-name").clients().get(idOfClient).roles().get(roleName).toRepresentation();
To listing all user:
UsersResource users = keycloak.realm("realm-name").users();
And "authorization" is the string token bearer
"getInstance" have other methods to send for example pass and user.
If anyone else is still struggling, here's a complete answer:
Create a security context producer:
#RequestScoped
public class SecurityContextProducer {
#Inject
private HttpServletRequest request;
#Produces
public KeycloakSecurityContext getSecurityContext() {
return ((KeycloakPrincipal) request.getUserPrincipal())
.getKeycloakSecurityContext();
}
}
Use it like this:
#Inject
private KeycloakSecurityContext keycloakSecurityContext;
public List<String> getRolesKeycloak() {
Set<String> roleNames = keycloakSecurityContext.getToken().getRealmAccess().getRoles();
List<String> targetList = new ArrayList<>(roleNames);
return targetList;
}
It's not exactly the topic but I needed to find the roles associated with a specific user and this question pops first with my keywords web search. Here's what worked for me with keycloak client 13.0.1
RealmResource realmResource = keycloak.realm(REALM);
UsersResource usersResource = realmResource.users();
UserResource userResource = usersResource.get(USER_ID);
RoleMappingResource roleMappingResource = userResource.roles();
// either realmLevel or clientLevel
RoleScopeResource roleScopeResource = roleMappingResource.realmLevel();
List<RoleRepresentation> rolesRepresentation = roleScopeResource.listAll();
I didn't find it elsewhere, I hope it can be useful.
I have a requirement in our project to sign-in user using other providers like facebook,google. For this, i am using social auth plugin. it is working fine with facebook,but with googleplus , i am getting error "State parameter value does not match with expected value", this error comes when user redirect in our app after google, means in getUserProfile().so how can i resolve this.
Dependencies are :
runtime "org.brickred:socialauth:4.7"
compile "org.brickred:socialauth:4.7"
my socialauth controller is
def authenticate(){
SocialAuthConfig config = SocialAuthConfig.getDefault()
//You can also pass input stream, properties object or properties file name.
config.load()
//Create an instance of SocialAuthManager and set config
SocialAuthManager manager = new SocialAuthManager()
manager.setSocialAuthConfig(config)
// URL of YOUR application which will be called after authentication
String successUrl = grailsApplication.config.auth.redirect.url
// get Provider URL to which you should redirect for authentication.
// id can have values "facebook", "twitter", "yahoo" etc. or the OpenID URL
String url = manager.getAuthenticationUrl(params.id, successUrl)
session.setAttribute("authManager", manager)
redirect (url:url)
}
#Secured('permitAll')
def getUserProfile(){
try{
// get the auth provider manager from session
SocialAuthManager manager = (SocialAuthManager)session.getAttribute("authManager");
// Pass request parameter map while calling connect method.
Map<String, String> paramsMap = SocialAuthUtil.getRequestParametersMap(request);
// call connect method of manager which returns the provider object.
AuthProvider provider = manager.connect(paramsMap);
Profile profile = provider.getUserProfile();
log.debug"user profile"+profile
// log.debug"contact"+ provider.getContactList()
}
catch(SocialAuthManagerStateException exception){
log.error("Exception occurs while connecting with SocialAuthManager--->"+exception.getMessage())
}
}
properties file
#googleplus
googleapis.com.consumer_key = XXXXXXXXXXXXXX
googleapis.com.consumer_secret = XXXXXXXXXXXXXXX
This is the problem with Version , you can use social auth 4.6 , it works fine
I am using a (little modified) workaround from this course, to fetch the userId, which is null if the request was sent from an Android client.
/**
* This is an ugly workaround for null userId for Android clients.
*
* #param user A User object injected by the cloud endpoints.
* #return the App Engine userId for the user.
*/
private static String getUserId(User user) {
String userId = user.getUserId();
if (userId == null) {
LOG.info("userId is null, so trying to obtain it from the datastore.");
AppEngineUser appEngineUser = new AppEngineUser(user);
ofy().save().entity(appEngineUser).now();
AppEngineUser savedUser = ofy().load().key(appEngineUser.getKey()).now();
userId = savedUser.getUser().getUserId();
LOG.info("Obtained the userId: " + userId);
}
return userId;
}
Although I am not able to get the userId.
INFO: Obtained the userId: null
This workaround has already worked perfectly in other projects, so the problem must be elsewhere. My endpoints api is annotated with the following scopes, clientIds and audiences:
scopes = {
Constants.EMAIL_SCOPE
},
clientIds = {
Constants.API_EXPLORER_CLIENT_ID,
Constants.WEB_CLIENT_ID,
Constants.ANDROID_CLIENT_ID
},
audiences = {
Constants.ANDROID_AUDIENCE
}
Constants.ANDROID_AUDIENCE and Constants.WEB_CLIENT_ID are the same. I am not using a web client, but Google told me to add a web client id. Does this client id need to have redirect uris and javascript origins specified?
In my Android client I am using the following to specify the audience.
mCredential = GoogleAccountCredential.usingAudience(
EndpointService.this,
"server:client_id:IDIDIDID.apps.googleusercontent.com"
);
Please help me to figure this one out.
I just understood why this workaround works. I need to begin a new objectify session so the cache is not used and the userId can be populated.
Objectify objectify = ofy().factory().begin();
AppEngineUser savedUser = objectify.load();