I'm trying to synchronize user names and primary emails with AD via the Github API. I make a call to /user/emails using an oAuth token with scope user:email and I get back their primary email. I then try to use PATCH /user per this doc with the payload:
{
email: newemail#ourcompany.com
}
And I get back a 404.
Is this the correct method to set the primary emails for users?
This is due to your API request is not properly authenticated or your OAuth token does not have the required scopes. As per your question, you have set user:email scope which does not allow modification of the profile. Use user scope intead.
See more: Why am I getting a 404 error on a repository that exists?
It requires authentication and it will return 404 Not Found, instead of 403 Forbidden, in some places. This is to prevent the accidental leakage of private repositories to unauthorized users.
You can try to make your email visibility to be public
[
{
"email": "newemail#ourcompany.com",
"primary": true,
"verified": true,
"visibility": "public"
}
]
Turns out the issue was a typo in the PATCH URL.
Related
Trying to bulk dismiss all risky signons so my security team can start new.
It should be able to be done using this powershell code:
get-riskyUser -Top 5 -Filter "Riskstate eq 'AtRisk'" -Orderby RiskLastUpdatedDateTime | Invoke-DismissRiskyUser
But I get this error 5 times:
VERBOSE: Performing the operation "Invoke-DismissRiskyUser_Dismiss" on target "Call remote 'RiskyUsersDismiss' operation".
Invoke-DismissRiskyUser_Dismiss: You cannot perform the requested operation.
I did authorize the scope for connect-graph with "IdentityRiskyUser.ReadWrite.All" as well assigned a P2 license to my admin account (which I think is stupid given the feature is a P1 feature)
This cmdlet calls the following API:
https://learn.microsoft.com/en-us/graph/api/riskyusers-dismiss?view=graph-rest-beta&tabs=http
so I dug up postman, got my self a shinny new bearer token based on a freshly minted app registration with the "IdentityRiskyUser.ReadWrite.All" API via app delegation which was then admin consented to then posted to the API using this body:
{
"userIds": [
"d9460130-07f0-43a9-9faa-df8c6635f4b7",
"faff0a2a-143e-431d-bd44-99db3f4fc30d"
]
}
and got back:
{
"error": {
"code": "BadRequest",
"message": "Invalid user ids.",
"innerError": {
"request-id": "a1dcf05e-bbf2-436a-9614-2a2d5b5d0fc0",
"date": "2019-12-06T19:59:03"
}
}
}
I triple checked those IDs are valid, also tried all lower case in the attribute name.
At a loss for what to do next, anyone have any thoughts and/or where I can post to github to open a ticket against the code?
Turns out a bunch of issues here. The PowerShell code didnt work because somehow I authorized read not readwrite on my first goes at it.
But thats not the whole story, next up, both the API and PowerShell were returning user object IDs for users no longer in Azure AD. These can never be dismissed via code nor the actual Azure Portal. I have a MS Case open for this.
I'm trying to make a Gogle Chrome extension that requires user authorization to a SugarCRM 7.5 instance with OAuth 2.0 and I need to store the access token that's retrieved. I may need more clarification on how launchWebAuthFlow works.
Firstly, I can retrieve an access token from SugarCRM by using a POST request (not a GET request) that returns an access and a refresh token.
When I tried using the code below I kept getting the error: "authorization url can not be loaded" and when I checked the background console (I already know that my auth URL is wrong). Any help is appreciated even if you don't know SugarCRM. Just a general answer that can get me started is much appreciated.
manifest.json
{
"name": "Auth Sample",
"version": "1",
"manifest_version": 2,
"minimum_chrome_version": "29",
"key": "<long key>",
"app": {
"background": {
"scripts": ["background.js"]
}
},
"permissions": [
"identity",
"https://<sugar instance>/*"
]
}
Background.js
chrome.identity.launchWebAuthFlow(
{'url': '<url-to-do-auth>', 'interactive': true},
function(redirect_url) { console.log(redirect_url); });
My URL is definitely not gonna work here but it's something like this:
var client_id = '<client id from Oauth 2.0>';
var redirectUri = chrome.identity.getRedirectURL("sugarcrm");
var url = "https://<sugar instance base url>/rest/v10/oauth2/token?client_id=" + client_id + "&callbackURL=" + redirectUri + "&response_type=token"
What's the url-to-do-auth (is this the URL to do a regular login (username and password) to SugarCRM or is it a GET request through the REST API to do login)
I don't have a UI web auth flow for my SugarCRM instance. Is it possible to create one from the application or should that be on the server?
I can only make POST requests to get an access token. Will that still work with launchWebAuthFlow?
Update
I ended up storing the token using the local chrome.storage api and only stored the refresh token which will keep getting a new access token every time the application is run(I think it's more secure than just storing the access token as that will force it to always keep changing without passing other credentials)
After more search on launchWebAuthFlow. I found out that the url launchWebAuthFlow takes as parameter will launch a web page(with interactive parameter set to true) which is hosted on the server, that will let the user login and will return an access token if success. This url is actually an endpoint on the restful server. You need to create this endpoint that will be a get request with some parameters including a callbackURL, client_id and response_type. Then it will respond with another function that will be a post request and will take the username and password. If credentials are correct, it will return the access token as a parameter in the callbackURL(chrome extension specific url that contains the extension ID).
Please feel free to correct me or add something if I'm wrong.
I've seen other stack questions but their solutions dont appear to be helping me at all.
I am trying to get profile information when I have an external access token that has been returned from an oauth login using
Microsoft.Owin.Security.Facebook
I have the returned access token and now want to request get some of the user profile data.
In post man I have tried the following GET
https://graph.facebook.com/v2.5/{user-id}?fields=about,name,email&access_token={token}
where I got the user-id by calling
https://graph.facebook.com/v2.5/me?access_token={token}
(which i think is an unnecessary step).
I have also tried the following
https://graph.facebook.com/v2.5/me?fields=about,name,email&access_token={token}
In every instance I only get the id and name fields returned. However, I have noticed if I do the following;
https://graph.facebook.com/v2.5/me?fields=email&access_token={token}
only the id is returned (i assume as I have not specifically asked for the name).
Any ideas why I am not being returned the data please?
UPDATED
If I call the following end point;
https://graph.facebook.com/v2.5/{user-id}/permissions?access_token={token}
I get the following data returned;
{
"data": [
{
"permission": "public_profile",
"status": "granted"
}
]
}
So I assume that I need to request the permissions of the app?
Ok, The ASP.NET permissions request isn't very clear on the facebook developers page. The issue is that the request for information is set in the
FacebookAuthenticationOptions
set during the startup of the application. The additional permissions are request at this point by add to the scope;
var options = new FacebookAuthenticationOptions
{
AppId = "Your App ID",
AppSecret = "Your App Secret",
};
options.Scope.Add("user_friends");
options.Scope.Add("email");
Also, to note during the request the fields are not sent by default and must be requested.
Links to the available permissions can be found here;
https://developers.facebook.com/docs/facebook-login/permissions
I'm using Sails with Passport for authentication. I'm using passport-google-oauth(OAuth2Strategy) and passport-facebook for enabling Google Sign-in.
I'm not too well-versed with Passport, so pardon me if this is a rookie question. I've set up login via Facebook and it works just fine. With Google, I do receive an authorization code after allowing access to the app, but the I'm eventually not authenticated. I'm guessing the same code should work for both Facebook and Google since the strategies are both based on oauth2.
I'm not even sure what code to share, since I'm using the auto-generated code from sails-generate-auth, but do let me know if there's anything else I can share.
Any ideas on why this might be happening? The app is locally hosted but that's unlikely to be the problem since I am getting to the authorization stage anyway.
I faced the same problem and it was located here in in api/services/passport.js:
// If the profile object contains a list of emails, grab the first one and
// add it to the user.
if (profile.hasOwnProperty('emails')) {
user.email = profile.emails[0].value;
}
// If the profile object contains a username, add it to the user.
if (profile.hasOwnProperty('username')) {
user.username = profile.username;
}
// If neither an email or a username was available in the profile, we don't
// have a way of identifying the user in the future. Throw an error and let
// whoever's next in the line take care of it.
if (!user.username && !user.email) {
return next(new Error('Neither a username nor email was available'));
}
The Google service was not returning a profile.username property.
Because of it, the user is not saved in the database and cannot be authenticated. Then the passport callback receives an empty user, so the function that handles errors is fired and the user is redirected to the login page.
This change allows to use the displayName property as the username:
// If the profile object contains a list of emails, grab the first one and
// add it to the user.
if (profile.hasOwnProperty('emails')) {
user.email = profile.emails[0].value;
}
// If the profile object contains a username, add it to the user.
if (profile.hasOwnProperty('username')) {
user.username = profile.username;
}
/** Content not generated BEGIN */
// If the username property was empty and the profile object
// contains a property "displayName", add it to the user.
if (!user.username && profile.hasOwnProperty('displayName')) {
console.log(profile); // <= Use it to check the content given by Google about the user
user.username = profile.displayName;
}
/** Content not generated END */
// If neither an email or a username was available in the profile, we don't
// have a way of identifying the user in the future. Throw an error and let
// whoever's next in the line take care of it.
if (!user.username && !user.email) {
return next(new Error('Neither a username nor email was available'));
}
You could also use the profile.id property because profile.displayName is not necessarily unique (ie: two Google accounts can have an identical displayName). But it is also true accross different services: a Twitter account could also have the same username than a Facebook account. If both register on your application, you will have a bug. This is a problem from the code generated by sails-generate-auth and you should adapt it with the behavior that you want.
I will propose a PR if this solution works for you too.
Alright, so this ultimately turned out to be a known issue with the API.
TL;DR: Enable the Google+ API and the Contacts API as mentioned here. (The Contacts API isn't required, as #AlexisN-o pointed out in the comments. My setup worked as desired with Contacts API disabled. This obviously depends on what scope you're using.)
I believe it's not a nice way of failing since this was an API error that was prevented from bubbling up. Anyway, I dug into passport.authenticate to figure out what was going wrong. This eventually calls the authenticate method defined in the package corresponding to the strategy (oauth2 in this case). In here (passport-google-oauth/lib/passport-google-oauth/oauth2.js) I found that the accessToken was indeed being fetched from Google, so things should be working. This indicated that there was a problem with the requests being made to the token urls. So I ventured a little further into passport-oauth2/lib/strategy.js and finally managed to log this error:
{ [InternalOAuthError: failed to fetch user profile]
name: 'InternalOAuthError',
message: 'failed to fetch user profile',
oauthError:
{ statusCode: 403,
data: '{
"error": {
"errors": [{
"domain": "usageLimits",
"reason": "accessNotConfigured",
"message": "Access Not Configured. The API (Google+ API) is not enabled for your project. Please use the Google Developers Console to update your configuration.",
"extendedHelp": "https://console.developers.google.com"
}],
"code": 403,
"message": "Access Not Configured. The API (Google+ API) is not enabled for your project. Please use the Google Developers Console to update your configuration."
}
}'
} }
This was the end of the hunt for me and the first result for the error search led to the correct answer. Weird fix though.
There's only thing that server has to do; just check any access token's validity.
Clients send to the server user id and access token obtained by FB.getLoginStatus. As I expected, there would be any URL that checks access token's validity, like http://xxx.facebook.com/access_token?=xxxxxxxxxxxxxxxxxxxxxxxxxxxx.
That returns whether it's available one or not or is there any API (server side) for that?
The officially supported method for this is:
GET graph.facebook.com/debug_token?
input_token={token-to-inspect}
&access_token={app-token-or-admin-token}
See the check token docs for more information.
An example response is:
{
"data": {
"app_id": 138483919580948,
"application": "Social Cafe",
"expires_at": 1352419328,
"is_valid": true,
"issued_at": 1347235328,
"metadata": {
"sso": "iphone-safari"
},
"scopes": [
"email",
"publish_actions"
],
"user_id": 1207059
}
}
You can simply request https://graph.facebook.com/me?access_token=xxxxxxxxxxxxxxxxx if you get an error, the token is invalid. If you get a JSON object with an id property then it is valid.
Unfortunately this will only tell you if your token is valid, not if it came from your app.
Just wanted to let you know that up until today I was first obtaining an app access token (via GET request to Facebook), and then using the received token as the app-token-or-admin-token in:
GET graph.facebook.com/debug_token?
input_token={token-to-inspect}
&access_token={app-token-or-admin-token}
However, I just realized a better way of doing this (with the added benefit of requiring one less GET request):
GET graph.facebook.com/debug_token?
input_token={token-to-inspect}
&access_token={app_id}|{app_secret}
As described in Facebook's documentation for Access Tokens here.
Simply request (HTTP GET):
https://graph.facebook.com/USER_ID/access_token=xxxxxxxxxxxxxxxxx
That's it.
The app token can be found from this url.
https://developers.facebook.com/tools/accesstoken
I found this official tool from facebook developer page, this page will you following information related to access token - App ID, Type, App-Scoped,User last installed this app via, Issued, Expires, Data Access Expires, Valid, Origin, Scopes.
Just need access token.
https://developers.facebook.com/tools/debug/accesstoken/
Exchange Access Token for Mobile Number and Country Code (Server Side OR Client Side)
You can get the mobile number with your access_token with this API https://graph.accountkit.com/v1.1/me/?access_token=xxxxxxxxxxxx. Maybe, once you have the mobile number and the id, you can work with it to verify the user with your server & database.
xxxxxxxxxx above is the Access Token
Example Response :
{
"id": "61940819992708",
"phone": {
"number": "+91XX82923912",
"country_prefix": "91",
"national_number": "XX82923912"
}
}
Exchange Auth Code for Access Token (Server Side)
If you have an Auth Code instead, you can first get the Access Token with this API - https://graph.accountkit.com/v1.1/access_token?grant_type=authorization_code&code=xxxxxxxxxx&access_token=AA|yyyyyyyyyy|zzzzzzzzzz
xxxxxxxxxx, yyyyyyyyyy and zzzzzzzzzz above are the Auth Code, App ID and App Secret respectively.
Example Response
{
"id": "619XX819992708",
"access_token": "EMAWdcsi711meGS2qQpNk4XBTwUBIDtqYAKoZBbBZAEZCZAXyWVbqvKUyKgDZBniZBFwKVyoVGHXnquCcikBqc9ROF2qAxLRrqBYAvXknwND3dhHU0iLZCRwBNHNlyQZD",
"token_refresh_interval_sec": XX92000
}
Note - This is preferred on the server-side since the API requires the APP Secret which is not meant to be shared for security reasons.
Good Luck.