I have an existing user in my Xamarin Forms app whose Details are as follows:
First Name: Jim
Last Name: Smith
ProviderLoginId = jsmith#google.com
OAuth Provider: Google
When I first create this user, I use the following method to authenticate against google as follows:
result = await AuthenticationClient.AcquireTokenInteractive("https://***.onmicrosoft.com/profile.read/profile.read")
.ExecuteAsync();
JObject objToken = new JObject();
objToken.Add("access_token", result.IdToken);
MobileServiceUser user = await App.syncMgr.CurrentClient.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, objToken);
Now, I take the UserId property from the MobileServiceUser user variable and store it into my User Table as primary key.
Subsequently, I create another new user with the following details:
First Name: Jim
Last Name: Smith
ProviderLoginId = jsmith#hotmail.com
OAuthProvider: Microsoft
When I create the user using the same steps as above, I get the same UserId back from Azure. Obviously, I am not able to store it in my User table for the second user. This only happens when the first name and the last name are the same regardless of which ProviderLoginId was used (whether it was Google OAuth or Microsoft OAuth).
Should I not be getting a unique UserId in each case? Since the bug surfaces only when the first and last names are same, I am assuming it is some sort of a bug.
Any and all help is appreciated.
You are using the older client, which is no longer supported.
You should be submitting the access token to the service, not the IdToken. You haven't stated what the AuthenticationClient is, but hopefully it is ADAL (since the older service doesn't work with MSAL).
The newer client and service completely abstracts the authentication code, allowing you to use whatever authentication mechanism you like and just dealing with standard OAuth2/OIDC and bearer tokens (Authorization header) instead of the non-standard X-ZUMO-AUTH header that was used in the past. My recommendation is to upgrade the client and server.
Related
I'm trying to implement the "on behalf of" flow in an application using ADFS 2016 as STS. As a reference, I look at this Microsoft tutorial (https://learn.microsoft.com/en-ca/windows-server/identity/ad-fs/development/ad-fs-on-behalf-of-authentication-in-windows-server). It's working as it should, I can login into my web application and then use my original access token in UserAssertion to generate a new access token with the proper audience to call my API BUT I found absolutely no way to include any user informations (sub, name, email, upn etc.) into the access token for my API, even if I set claim rules into my ADFS configurations for the API.
I checked the communication between my app and adfs using Fiddler and everything looks like the informations in the tutorial. See the screen shot of the "on behalf of" request below :
Here's the resulting access token :
Finally, here's the code I use to generate my new access token :
private async Task<string> GetAccessToken(ClaimsPrincipal user, string originalAccessToken)
{
var authority = "[authority]";
var context = new AuthenticationContext(authority, false);
string userName = user.FindFirstValue("upn");
var userAssertion = new UserAssertion(originalAccessToken, "urn:ietf:params:oauth:grant-type:jwt-bearer",userName);
var cc = new ClientCredential("https://localhost:44387/", "[client_secret]");
var result = await context.AcquireTokenAsync("https://localhost:44339/", cc, userAssertion);
return result.AccessToken;
}
Have you struggle with that scenario and if yes, did you find a way to fix this ?
Thanks
I've only used the Microsoft On Behalf Of flow with Azure AD and not ADFS, but it looks like you need to send a more detailed scope in your User Info request.
Maybe try sending 'openid profile email', to indicate that you want that type of detail, as in Section 17 of my blog post. Of course this assumes that this type of data has been registered for all users.
TROUBLESHOOTING
Looks like one of these will be the cause:
A suboptimal Microsoft library that does not allow you to send the required scope
Or ADFS 2016 perhaps lacks the scope features that work correctly in Azure AD
I would concentrate on making extra sure you are sending the correct form URL encoded request message, using a tool such as curl, Postman or a plain C# HttpClient. Here is the code I used to send the correct scope - using an open source library rather than a Microsoft one:
Sample NodeJS Code
If you can get the scope sent correctly then you should have a resolution either way:
Either you get the correct data and can update your code
Or the behaviour you want is not supported by ADFS
Good luck ...
I have been trying to use Azure AD MSAL and ADAL and have NEVER been able to retrieve a token. I have tried the ALL of the samples and keep getting to the same issue, token is created, added to the EF cache DB but when the tokenAcquisition object tries to retrieve it, no account is found and fails to get token.
I have read through most (if not ALL) of the issues on GitHub and SO. this seems to be working for others but looks like numerous people have the same issue and I have yet to see an answer other then pointing me to the samples I have tried.
Simple question for the moment - how do I get accounts from the IConfidentialClientApplication?
I have NEVER been able to get a single account or a list of accounts.
Create app object:
var app = ConfidentialClientApplicationBuilder.CreateWithApplicationOptions(_applicationOptions)
.WithRedirectUri(currentUri)
.WithAuthority(authority)
.Build();
In GetAccessTokenOnBehalfOfUser:
IAccount account = await application.GetAccountAsync(accountIdentifier);
returns NULL
and
var accounts = await application.GetAccountsAsync();
returns an empty lists/IEnumerable.
I would expect to retrieve an account from
application.GetAccountAsync(accountIdentifier)
and a list from
await application.GetAccountsAsync();
OK, Finally found my issue.
The issue comes in using ASPNet identity logging into AzureAD as an external authority but using the identity to signin and create the claims principle.
I was mssing the AzureAD ObjectIdentifier from my claims. so the solution seems to be adding the ObjectIdentifier to the identity. I did this by using a ClaimsTransofrmation and looking for the auth type. If it was NOT Identity.Application it is from AzureAD and check to see if the User has the UserClaim and add it if not. This claim is then picked up and put in the principle's claims and under the covers, now the account is found....
if (principal.HasClaim(c => c.Type == SecurityConstants.ClaimTypes.ObjectId))
{
string oId = principal.FindFirstValue(SecurityConstants.ScpcClaimTypes.ObjectId);
var user = _usrMgr.FindByNameAsync(usrNm).Result;
List<Claim> claims = new List<Claim>(_usrMgr.GetClaimsAsync(user).Result);
if (!claims.Exists(c => c.Type == SecurityConstants.ScpcClaimTypes.ObjectId))
{
_usrMgr.AddClaimAsync(user, new Claim(SecurityConstants.ScpcClaimTypes.ObjectId, oId));
}
Cosmos DB, API Azure Tables, gives you 2 endpoints in the Overview blade
Document Endpoint
Azure Table Endpoint
An example of (1) is
https://myname.documents.azure.com/dbs/tempdb/colls
An example of (2) is
https://myname.table.cosmosdb.azure.com/FirstTestTable?$filter=PartitionKey%20eq%20'car'%20and%20RowKey%20eq%20'124'
You can create the authorization code for (1) on the client using the prerequest code from this Postman script: https://github.com/MicrosoftCSA/documentdb-postman-collection/blob/master/DocumentDB.postman_collection.json
Which will give you a code like this:
Authorization: type%3Dmaster%26ver%3D1.0%26sig%3DavFQkBscU...
This is useful for playing with the rest urls
For (2) the only code I could find to generate a code that works was on the server side and gives you a code like this:
Authorization: SharedKey myname:JXkSGZlcB1gX8Mjuu...
I had to get this out of Fiddler
My questions
(i) Can you generate a code for case (2) above on the client like you can for case (1)
(ii) Can you securely use Cosmos DB from the client?
If you go to the Azure Portal for a GA Table API account you won't see the document endpoint anymore. Instead only the Azure Table Endpoint is advertised (e.g. X.table.cosmosdb.azure.com). So we'll focus on that.
When using anything but direct mode with the .NET SDK, our existing SDKs when talking to X.table.cosmosdb.azure.com endpoint are using the SharedKey authentication scheme. There is also a SharedKeyLight scheme which should also work. Both are documented in https://learn.microsoft.com/en-us/rest/api/storageservices/authentication-for-the-azure-storage-services. Make sure you read the sections specifically on the Table Service. The thing to notice is that a SharedKey header is directly tied to the request it is associated with. So basically every request needs a unique header. This is useful for security because it means that a leaked header can only be used for a limited time to replay a specific request. It can't be used to authorize other requests. But of course that is exactly what you are trying to do.
An alternative is the SharedKeyLight header which is a bit easier to implement as it just requires a date and the a URL.
But we don't have externalized code libraries to really help with either.
But there is another solution that is much friendly to things like Fiddler or Postman, which is to use a SAS URL as defined in https://blogs.msdn.microsoft.com/windowsazurestorage/2012/06/12/introducing-table-sas-shared-access-signature-queue-sas-and-update-to-blob-sas/.
There are at least two ways to get a SAS token. One way is to generate one yourself. Here is some sample code to do that:
var connectionString = "DefaultEndpointsProtocol=https;AccountName=tableaccount;AccountKey=X;TableEndpoint=https://tableaccount.table.cosmosdb.azure.com:443/;";
var tableName = "ATable";
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString);
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable table = tableClient.GetTableReference(tableName);
await table.CreateIfNotExistsAsync();
SharedAccessTablePolicy policy = new SharedAccessTablePolicy()
{
SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(1000),
Permissions = SharedAccessTablePermissions.Add
| SharedAccessTablePermissions.Query
| SharedAccessTablePermissions.Update
| SharedAccessTablePermissions.Delete
};
string sasToken = table.GetSharedAccessSignature(
policy, null, null, null, null, null);
This returns the query portion of the URL you will need to create a SAS URL.
Another, code free way, to get a SAS URL is to go to https://azure.microsoft.com/en-us/features/storage-explorer/ and download the Azure Storage Explorer. When you start it up it will show you the "Connect to Azure Storage" dialog. In that case:
Select "Use a connection string or a shared access signature URI" and click next
Select "Use a connection string" and paste in your connection string from the Azure Portal for your Azure Cosmos DB Table API account and click Next and then click Connect in the next dialog
In the Explorer pane on the left look for your account under "Storage Accounts" (NOT Cosmos DB Accounts (Preview)) and then click on Tables and then right click on the specific table you want to explore. In the right click dialog you will see an entry for "Get Shared Access Signature", click on that.
A new dialog titled "Generate Shared Access Signature" will show up. Unfortunately so will an error dialog complaining about "NotImplemented", you can ignore that. Just click OK on the error dialog.
Now you can choose how to configure your SAS, I usually just take the defaults since that gives the widest access permission. Now click Create.
The result will be a dialog with both a complete URL and a query string.
So now we can take that URL (or create it ourselves using the query output from the code) and create a fiddler request:
GET https://tableaccount.table.cosmosdb.azure.com/ATable?se=2018-01-12T05%3A22%3A00Z&sp=raud&sv=2017-04-17&tn=atable&sig=X&$filter=PartitionKey%20eq%20'Foo'%20and%20RowKey%20eq%20'bar' HTTP/1.1
User-Agent: Fiddler
Host: tableaccount.table.cosmosdb.azure.com
Accept: application/json;odata=nometadata
DataServiceVersion: 3.0
To make the request more interesting I added a $filter operation. This is an OData filter that lets us explore the content. Note, btw, to make filter work both the Accept and DataServiceVersion headers are needed. But you can use the base URL (e.g. without the filter parameter) to make any of the REST API calls on a specific table.
Do be aware that the SAS token is scoped to an individual table. So higher level operations won't work with this SAS token.
I am using Twitterizer to integrate Twitter into my ASP.NET app. After authenticating the user I am using following code to see if the authenticated user is following a specific user:
Twitterizer.TwitterUser followed = new Twitterizer.TwitterUser();
followed.ScreenName = tw_current_like;
bool? Following = followed.IsFollowing;
However Following.HasValue is always false. Am I missing something?
As I stated on the forums. Instantiating TwitterUser will not fetch any data from Twitter.
Also, the Twitter API is spotty in how/when it supplies the following indicator. You can use TwitterFriendship.Show(OAuthTokens, decimal/string) to query details about the relationship between the authenticated user and another user.
I need to check if user has granted a given permission (for example: publish_stream) in server-side. It seams for client-side exists solution but what I need is for server-side.
Any idea please?
You need a server side API like the Facebook Java API or restFB. These are java APIs (that I have used), there a host of others for different languages. The official ones are shown on the Facebook developer portal.
Using these APIs you will be able to do more than just authenticate a user on the server side. Follow the link to the different pages and you'll find snippets that will get you started in no time!
EDIT
Since you're using ASP, you'll need an ASP .NET API. This is the first result found from a google search http://facebooksdk.codeplex.com/ .
Using this API and an MVC architecture, you are able to annotate any controller actions that contain code that requires permissions with something like [CanvasAuthorize(Perms = "publish_stream")] and when the user calls that action they'll need to provide permission.
I cant give more details as I've never used this API but do check out this in-depth tutorial on building a sample application.
You can make a Graph API call to this URL https://graph.facebook.com/me/permissions including the user access token. You will get results with the user's permissions. For more details see https://developers.facebook.com/docs/reference/api/user/
Solution:
Step 1. First u must make a query:
query is like: SELECT {0} FROM permissions WHERE uid = {1}
{0} is comma-separated permissions. for example: publish_stream,offline_access
{1} is userId
Step 2. Then send this query to the given url:
url: https://api.facebook.com/method/fql.query?query={0}&format=JSON&access_token={1}
{0} is query that we made in step1
{1} is user's AccessToken
Sample Result: [{"publish_stream":1,"offline_access":0}]
Update Another way with using Graph Api:
https://graph.facebook.com/me/permissions?access_token={0}
{0} is user's AccessToken
Sample Result:
{
"data": [
{
"installed": 1,
"status_update": 1,
"photo_upload": 1,
"video_upload": 1,
"offline_access": 1,
"create_note": 1,
"share_item": 1,
"bookmarked": 1,
"publish_stream": 1
}
]
}