Hangout OAuth - Invalid Scope : Some requested scopes cannot be shown - google-oauth

I am facing the below error while generating token for service account for the Hangout Scope - https://www.googleapis.com/auth/chat.bot.
Where i receive 400 response code after making a post request to this url -
https://www.googleapis.com/oauth2/v4/token
the params are
Content-Type:application/x-www-form-urlencoded
httpMode:POST
body:grant_type=jwt-bearer&assertion=assertion-token
Note:This was completely working fine. Suddenly am facing this issue.
cross verified: jwt generation,service_account_id and etc...
Error Response : { "error": "invalid_scope", "error_description": "Some requested scopes cannot be shown": [https://www.googleapis.com/auth/chat.bot]}
code for generating assertion:
//FORMING THE JWT HEADER
JSONObject header = new JSONObject();
header.put("alg", "RS256");
header.put("typ", "JWT");
//ENCODING THE HEADER
String encodedHeader = new String(encodeUrlSafe(header.toString().getBytes("UTF-8")));
//FORMING THE JWT CLAIM SET
JSONObject claimSet = new JSONObject();
claimSet.put("iss","123#hangout.iam.gserviceaccount.com");
claimSet.put("sub","one#domain.com");
claimSet.put("scope","https://www.googleapis.com/auth/chat.bot");
claimSet.put("aud","https://oauth2.googleapis.com/token");
long time = System.currentTimeMillis() / 1000;
claimSet.put("exp",time+3600);
claimSet.put("iat",time);
//ENCODING THE CLAIM SET
String encodedClaim = new String(encodeUrlSafe(claimSet.toString().getBytes("UTF-8")));
//GENERATING THE SIGNATURE
String password = "secretofkey", alias = "privatekey";
String signInput = encodedHeader + "." + encodedClaim;
Signature signature = Signature.getInstance("SHA256withRSA");
String filepath = "/check/PrivateKeys/hangoutPKEY.p12";
KeyStore kstore = KeyStore.getInstance("PKCS12");
fis = new FileInputStream(filepath);
kstore.load(fis, password.toCharArray());
KeyStore.PrivateKeyEntry pke = (KeyStore.PrivateKeyEntry) kstore.getEntry(alias, new KeyStore.PasswordProtection(password.toCharArray()));
PrivateKey pKey = pke.getPrivateKey();
signature.initSign(pKey);
signature.update(signInput.getBytes("UTF-8"));
String encodedSign = new String(encodeUrlSafe(signature.sign()), "UTF-8");
//JWT GENERATION
String JWT = signInput + "." + encodedSign;
String grant_type = URLEncoder.encode("urn:ietf:params:oauth:grant-type:jwt-bearer");
reqBody = "grant_type=" + grant_type + "&assertion=" + JWT;
public static byte[] encodeUrlSafe(byte[] data) {
Base64 encoder = new Base64();
byte[] encode = encoder.encodeBase64(data);
for (int i = 0; i < encode.length; i++) {
if (encode[i] == '+') {
encode[i] = '-';
} else if (encode[i] == '/') {
encode[i] = '_';
}
}
return encode;
}
Does anyone have any idea, where am going wrong?

Short answer:
You are trying to use domain-wide authority to impersonate a regular account. This is not supported in Chat API.
Issue detail:
You are using the sub parameter when building your JWT claim:
claimSet.put("sub","one#domain.com");
Where sub refers to:
sub: The email address of the user for which the application is requesting delegated access.
I noticed that, if I add the sub parameter to my test code, I get the same error as you.
Solution:
Remove this line from your code in order to authorize with the service account (without impersonation) and handle bot data:
claimSet.put("sub","one#domain.com");
Background explanation:
Chat API can be used for bots to manage their own data, not to manage end-user data. Therefore, you can only use a service account to act as the bot, without impersonating an end-user.
From this Issue Tracker comment:
At the present moment, Chat API can only be used to manage bot-related data (listing the spaces in which the bot is included, etc.). Using domain-wide delegation to manage regular users' data is not currently possible.
Feature request:
If you'd like to be able to access regular users' data with your service account and domain-wide delegation via Chat API, you are not alone. This feature has been requested before in Issue Tracker:
Accessing the Google Chats of regular users using domain-wide delegated permission and service account credentials
I'd suggest you to star the referenced issue in order to keep track of it and to help prioritizing it.
Reference:
Using service accounts
Delegating domain-wide authority to the service account
Preparing to make an authorized API call

Related

IdentityServer4 - Best Practises to get User Information from access token

I recently started developing using IdentityServer4. What I want to achieve is to have a number of independent web applications that use the same authorization server, my identity server.
My problem is how to make sure, that all my independend web applications have obtained and display the up to date user information (like firstName,lastName,avatar etc) which are stored in my IdentityServer4 database
I am aware that I should implement the IProfileService interface, to make sure that user-info endpoint will return all additional user info, but I dont know where to call this api request from my web applications. I have created a function that looks like this:
var t = await HttpContext.GetTokenAsync("access_token");
if (!String.IsNullOrEmpty(t))
{
var client = new HttpClient();
var userInfoRequest = new UserInfoRequest()
{
Address = "https://localhost:5001/connect/userinfo",
Token = t
};
var response = client.GetUserInfoAsync(userInfoRequest).Result;
if (response.IsError)
throw new Exception("Invalid accessToken");
dynamic responseObject = JsonConvert.DeserializeObject(response.Raw);
string firstName = responseObject.FirstName.ToString();
HttpContext.Session.SetString("User_FirstName", firstName);
string lastName = responseObject.LastName.ToString();
HttpContext.Session.SetString("User_LastName", lastName);
HttpContext.Session.SetString("User_FullName", firstName + " " + lastName);
if (responseObject.Image != null && !String.IsNullOrEmpty(responseObject.Image.ToString()))
{
string im = responseObject.Image.ToString();
HttpContext.Session.SetString("User_Image", im);
}
}
to get user Info from web applications.
My problem is when and how to call this function, every time the user redirects logged in from identity server to my web application, and how to make sure that Sessions will keep user associated data, for as much as the user remains logged in to my web application.
You can call Token Introspection endpoint to get all user info from #identityServer4.

Google Sheets API v4 receives HTTP 401 responses for public feeds

I'm having no luck getting a response from v4 of the Google Sheets API when running against a public (i.e. "Published To The Web" AND shared with "Anyone On The Web") spreadsheet.
The relevant documentation states:
"If the request doesn't require authorization (such as a request for public data), then the application must provide either the API key or an OAuth 2.0 token, or both—whatever option is most convenient for you."
And to provide the API key, the documentation states:
"After you have an API key, your application can append the query parameter key=yourAPIKey to all request URLs."
So, I should be able to get a response listing the sheets in a public spreadsheet at the following URL:
https://sheets.googleapis.com/v4/spreadsheets/{spreadsheetId}?key={myAPIkey}
(with, obviously, the id and key supplied in the path and query string respectively)
However, when I do this, I get an HTTP 401 response:
{
error: {
code: 401,
message: "The request does not have valid authentication credentials.",
status: "UNAUTHENTICATED"
}
}
Can anyone else get this to work against a public workbook? If not, can anyone monitoring this thread from the Google side either comment or provide a working sample?
I managed to get this working. Even I was frustrated at first. And, this is not a bug. Here's how I did it:
First, enable these in your GDC to get rid of authentication errors.
-Google Apps Script Execution API
-Google Sheets API
Note: Make sure the Google account you used in GDC must be the same account you're using in Spreadsheet project else you might get a "The API Key and the authentication credential are from different projects" error message.
Go to https://developers.google.com/oauthplayground where you will acquire authorization tokens.
On Step 1, choose Google Sheets API v4 and choose https://www.googleapis.com/auth/spreadsheets scope so you have bot read and write permissions.
Click the Authorize APIs button. Allow the authentication and you'll proceed to Step 2.
On Step 2, click Exchange authorization code for tokens button. After that, proceed to Step 3.
On Step 3, time to paste your URL request. Since default server method is GET proceed and click Send the request button.
Note: Make sure your URL requests are the ones indicated in the Spreadsheetv4 docs.
Here's my sample URL request:
https://sheets.googleapis.com/v4/spreadsheets/SPREADSHEET_ID?includeGridData=false
I got a HTTP/1.1 200 OK and it displayed my requested data. This goes for all Spreadsheetv4 server-side processes.
Hope this helps.
We recently fixed this and it should now be working. Sorry for the troubles, please try again.
The document must be shared to "Anyone with the link" or "Public on the web". (Note: the publishing settings from "File -> Publish to the web" are irrelevant, unlike in the v3 API.)
This is not a solution of the problem but I think this is a good way to achieve the goal. On site http://embedded-lab.com/blog/post-data-google-sheets-using-esp8266/ I found how to update spreadsheet using Google Apps Script. This is an example with GET method. I will try to show you POST method with JSON format.
How to POST:
Create Google Spreadsheet, in the tab Tools > Script Editor paste following script. Modify the script by entering the appropriate spreadsheet ID and Sheet tab name (Line 27 and 28 in the script).
function doPost(e)
{
var success = false;
if (e != null)
{
var JSON_RawContent = e.postData.contents;
var PersonalData = JSON.parse(JSON_RawContent);
success = SaveData(
PersonalData.Name,
PersonalData.Age,
PersonalData.Phone
);
}
// Return plain text Output
return ContentService.createTextOutput("Data saved: " + success);
}
function SaveData(Name, Age, Phone)
{
try
{
var dateTime = new Date();
// Paste the URL of the Google Sheets starting from https thru /edit
// For e.g.: https://docs.google.com/---YOUR SPREADSHEET ID---/edit
var MyPersonalMatrix = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/---YOUR SPREADSHEET ID---/edit");
var MyBasicPersonalData = MyPersonalMatrix.getSheetByName("BasicPersonalData");
// Get last edited row
var row = MyBasicPersonalData.getLastRow() + 1;
MyBasicPersonalData.getRange("A" + row).setValue(Name);
MyBasicPersonalData.getRange("B" + row).setValue(Age);
MyBasicPersonalData.getRange("C" + row).setValue(Phone);
return true;
}
catch(error)
{
return false;
}
}
Now save the script and go to tab Publish > Deploy as Web App.
Execute the app as: Me xyz#gmail.com,
Who has access to the app: Anyone, even anonymous
Then to test you can use Postman app.
Or using UWP:
private async void Button_Click(object sender, RoutedEventArgs e)
{
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(#"https://script.google.com/");
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.AcceptEncoding.Add(new System.Net.Http.Headers.StringWithQualityHeaderValue("utf-8"));
string endpoint = #"/macros/s/---YOUR SCRIPT ID---/exec";
try
{
PersonalData personalData = new PersonalData();
personalData.Name = "Jarek";
personalData.Age = "34";
personalData.Phone = "111 222 333";
HttpContent httpContent = new StringContent(JsonConvert.SerializeObject(personalData), Encoding.UTF8, "application/json");
HttpResponseMessage httpResponseMessage = await httpClient.PostAsync(endpoint, httpContent);
if (httpResponseMessage.IsSuccessStatusCode)
{
string jsonResponse = await httpResponseMessage.Content.ReadAsStringAsync();
//do something with json response here
}
}
catch (Exception ex)
{
}
}
}
public class PersonalData
{
public string Name;
public string Age;
public string Phone;
}
To above code NuGet Newtonsoft.Json is required.
Result:
If your feed is public and you are using api key, make sure you are throwing a http GET request.In case of POST request, you will receive this error.
I faced same.
Getting data using
Method: spreadsheets.getByDataFilter has POST request

Identity Server 3 implicit grant, role based authorization

I have set up the Identity Server 3 with Membership reboot database as my authorization server and have also developed a Web Api project which will be accessed by a javascript web app.
Using the implicit flow, the client is able to log in and obtain id_token and access_token. Now I have a few questions, which I would appreciate some detailed answers too:
What is the functionality of id_token? After obtaining it, what can I do with it?
The roles of the users are stored in the database as claims (like for example, the key value of "role","admin"). How do I perform the role-based authorization at this point? It seems like the id_token contains those claims but the access_token does not. When sending my access_token as Bearer along my Api request, how does the api know which roles the sending user has?
In a web api controller, I want to access the user's information using:
var user = User as ClaimsPrincipal;
using this code, I cannot get pretty much anything about the user; username, id, etc. Also when I use user.Claims in the controller, I have no access to the claims stored in the database. How are there two sets of claims, one in the database one in the token?!
Any extra information is greatly appreciated.
id_token should be used in the client. You can use it to access the claims at client side. AccessToken is to be used at the API.
To the claims to be included in the access_token you need to create a scope with relevant claims and request that scope in the request.
To create a scope(in the self-host sample add scope to Scopes.cs):
new Scope
{
Name = "myApiScope",
DisplayName = "IdentityManager",
Type = ScopeType.Resource,
Emphasize = true,
ShowInDiscoveryDocument = false,
Claims = new List<ScopeClaim>
{
new ScopeClaim(Constants.ClaimTypes.Name),
new ScopeClaim(Constants.ClaimTypes.Role)
}
}
Ask for the scope in your authorization request(In Javascript implicit client - simple it is done as follows)
function getToken() {
var authorizationUrl = 'https://localhost:44333/core/connect/authorize';
var client_id = 'implicitclient';
var redirect_uri = 'http://localhost:37045/index.html';
var response_type = "token";
var scope = "myApiScope";
var state = Date.now() + "" + Math.random();
localStorage["state"] = state;
var url =
authorizationUrl + "?" +
"client_id=" + encodeURI(client_id) + "&" +
"redirect_uri=" + encodeURI(redirect_uri) + "&" +
"response_type=" + encodeURI(response_type) + "&" +
"scope=" + encodeURI(scope) + "&" +
"state=" + encodeURI(state);
window.location = url;
}
This will include Name and Role claims in your access token
Configure your API with relevant middleware in the web API startup(in SampleAspNetWebApi sample it is done as follows)
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "https://localhost:44333/core",
RequiredScopes = new[] { "myApiScope" }
});
Then you can access the claims as follows
var principal = User as ClaimsPrincipal;
return from c in principal.Identities.First().Claims
select new
{
c.Type,
c.Value
};

Create registration for Azure Notification Hub in Postman

I created a Service Bus / Notification Hub in my Azure Portal.
Now I'm trying to use the Azure REST API with Postman based on this doc :
https://msdn.microsoft.com/en-us/library/azure/dn223265.aspx
Here is the Postman configuration I have :
It's a POST method of the following url (Create Registration)
https://mysite.servicebus.windows.net/mysite-notif/registrations/?api-version=2015-01
(I replaced with mysite in that url for privacy reasons)
In the Headers, I typed 2 entries :
Content-Type
application/atom+xml;type=entry;charset=utf-8
Authorization
Endpoint=sb://[mysite].servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=[mykey]
(this Connection information I copied from the Azure portal)
In the Body, I chose raw - XML(txt/xml) and pasted :
<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom">
<content type="application/xml">
<WindowsRegistrationDescription xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect">
<Tags>myTag, myOtherTag</Tags>
<ChannelUri>{ChannelUri}</ChannelUri>
</WindowsRegistrationDescription>
</content>
</entry>
(it's the Native registration for Windows Notification Service example)
When I send this call from within Postman, I get a 401 Error :
<Error>
<Code>401</Code>
<Detail>MalformedToken: The credentials contained in the authorization header are not in the WRAP format..TrackingId:ee0d87ef-6175-46a1-9b35-6c31eed6049d_G2,TimeStamp:8/13/2015 9:58:26 AM</Detail>
</Error>
What am I missing ?
Is it the Authorization tab I left on "No Auth" in Postman ?
Is it the value of the Authorization header that should be encoded like shown here ?
Creating registration ID for Azure Notification Hub via REST api
Thanks.
Here is an example of a pre-request script for postman that generates the needed header:
function getAuthHeader(resourceUri, keyName, key) {
var d = new Date();
var sinceEpoch = Math.round(d.getTime() / 1000);
var expiry = (sinceEpoch + 3600);
var stringToSign = encodeURIComponent(resourceUri) + '\n' + expiry;
var hash = CryptoJS.HmacSHA256(stringToSign, key);
var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
var sasToken = 'SharedAccessSignature sr=' + encodeURIComponent(resourceUri) + '&sig=' + encodeURIComponent(hashInBase64) + '&se=' + expiry + '&skn=' + keyName;
return sasToken;
}
postman.setEnvironmentVariable('azure-authorization', getAuthHeader(request['url'], "mySharedAccessKeyName", "mySharedAccessKey"));
postman.setEnvironmentVariable('current-date',new Date().toUTCString());
To use it do the following:
add this pre-request script to your postman request
replace mySharedAccessKeyName , mySharedAccessKey with your credentials
add a header Authorization: {{azure-authorization}}
add a header x-ms-date: {{current-date}}
Your "Authorization" header is not correct.
As stated in the Azure Notification Hubs REST API documentation, e.g. for creating a registration, the "Authorization" header has to contain the "Token generated as specified in Shared Access Signature Authentication with Service Bus"...
The token format is specified in the documentation for Shared Access Signature Authentication with Service Bus as the following:
SharedAccessSignature sig=<signature-string>&se=<expiry>&skn=<keyName>&sr=<URL-encoded-resourceURI>
URL-encoded-resourceURI: The url you send the POST request to (in your case "https://mysite.servicebus.windows.net/mysite-notif/registrations/?api-version=2015-01")
keyName: In your case the default key name "DefaultFullSharedAccessSignature"
expiry: The expiry is represented as the number of seconds since the epoch 00:00:00 UTC on 1 January 1970.
signature-string: The signature for the SAS token is computed using the HMAC-SHA256 of a string-to-sign with the PrimaryKey property of an authorization rule. The string-to-sign consists of a resource URI and an expiry, formatted as follows:
StringToSign = <resourceURI> + "\n" + expiry;
resourceURI should be the same as URL-encoded-resourceURI (also URL encoded)
Compute the HMAC-SHA256 of StringToSign using the SAS key (what you replaces with [mykey] in your example). Use the URL encoded result for signature-string then.
After spending over an hour trying to understand why the steps above didn't work, I realized if you are using the code from https://code.msdn.microsoft.com/Shared-Access-Signature-0a88adf8 It has two things that are not defined at the top of the code. Key and KeyName.
The Key is the part that alluded me because at first glance on the other post here I thought it was the same. Its not.
In Azure: Go to your Notification Hub, Then Click > Settings> Access Policies then on the Policy that has Manage Permission. Add a policy if you need to. Once you Click on the Access Policy. It shows Connection String, Primary and Secondary. Copy the Primary to your Clipboard and throw it in notepad. It will look something like this..
Endpoint=sb://mysite.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=hc7qZ+pMG6zltjmASDFrskZO+Yv52D55KQUxUTSO0og=
SharedAccessKeyName = KeyName
SharedAccessKey = Key
Yea it looks obvious all spelled out here but you cannot see this information in AZURE portal unless you copy it.
So Just to be totally Clear, in the header you generate the key "sig" by combining + "\n" + expiry which Baris did point out, but then you sign it with the Key not the KeyName..
I may sounds like an idiot spelling this out but this process is not an easy one.
Hope it helps someone else.
Baris Akar's response is mostly correct, except for one omission that, for whatever reason, is also not mentioned in the relevant documentation: the signature parameter (i.e., the signature-string in sig=) must be Base64 encoded!
You have to remove "\"" in Token String like below.
authorizationString = resultA.replaceAll("\"","");
From
"SharedAccessSignature sr=https%3a%2f%2fmshub.servicebus.windows.net%2f&sig=PFZVab43PMsO0q9gz4%2bFsuaQq%5ff05L4M7hKVBN8DEn0%3d&se=1553339810&skn=RootManageSharedAccessKey"
To
SharedAccessSignature sr=https%3a%2f%2fmshub.servicebus.windows.net%2f&sig=PFZVab43PMsO0q9gz4%2bFsuaQq%5ff05L4M7hKVBN8DEn0%3d&se=1553339810&skn=RootManageSharedAccessKey
Good luck.
See the following documentation from Microsoft to generate a SAS Token.
This token you can use in Postman.
Generate SAS Token (NodeJs, Java, etc.)
Like Jérôme, I also used the example at https://code.msdn.microsoft.com/Shared-Access-Signature-0a88adf8 to generate the token and I also found out that the .NET-generated token worked. I compared the .NET-generated token with my ruby-generated token and found that URI.escape did not encode the last character (an '=' sign) of my base64 hash. It also did not encode '+' signs. Adding the string '=+' to the function fixed the problem: URI.escape(hmacb64, '=+')
(I don't know if there are other characters that should be identified here.)
It also took me quite some time to figure out a way to generate the SAS tokens in Go.
I created a gist which shows how to generate those tokens:
https://gist.github.com/dennis-tra/14c63e6359f17cbb504e78d6740ca465
I probably wouldn't have figured it out if had not found this repo:
https://github.com/shanepeckham/GenerateSASTokenGo/blob/master/gosas.go
Working from D-rk's code, which is probably outdated in 2022, here's an updated version that works in Postman 10.5.6 and with the Azure Notification Hub's api-version 2020-06
Postman Pre-Request Script:
function createSharedAccessToken(sb_name, eh_name, saName, saKey) {
if (!sb_name || !eh_name || !saName || !saKey) {
throw "Missing required parameter";
}
var resourceUri = encodeURIComponent("https://" + sb_name + ".servicebus.windows.net/" + eh_name)
// Set expiration in seconds
var expires = (Date.now() / 1000) + 20 * 60;
expires = Math.ceil(expires);
var toSign = CryptoJS.enc.Utf8.parse(resourceUri + '\n' + expires);
var sa_key_utf8 = CryptoJS.enc.Utf8.parse(saKey);
var hmac = CryptoJS.HmacSHA256(toSign, sa_key_utf8);
var hmacBase64 = CryptoJS.enc.Base64.stringify(hmac);
var hmacUriEncoded = encodeURIComponent(hmacBase64);
// Construct autorization string
var token = "SharedAccessSignature sr=" + resourceUri + "&sig=" + hmacUriEncoded + "&se=" + expires + "&skn="+ saName;
return token;
}
var sb_name = "your-notification-hub-namespace";
var eh_name = "your-notification-hub-name";
//See Access Policies -> Connection String
var sa_name = "your-shared-access-key-name"
var sa_key = "your-shared-access-key-name"
var auth_header = createSharedAccessToken(sb_name, eh_name, sa_name,sa_key);
pm.environment.set('azure-authorization',auth_header);
pm.environment.set('current-date',new Date().toUTCString());
Solution provided by Dirk helped me to resolve the issue.
But make sure to use SharedAccessKeyName and SharedAccessKey from a policy which has "Manage" claims access. If you have only Send and/or Listen claims, then the authentication will not work and throws an error - MalformedToken: The credentials contained in the authorization header are not in the WRAP format

Adding extra details to a webapi bearer token

I am trying to learn the new webapi2.1 authentication pieces.
I have got the bearer token wired up and working with my webapi. My next thing I would like to do is be able to store some additional information within the token (if possible) so when the client sends back the token I can retrieve the details without the need of them sending multiple values.
Can the token be extended to contain custom data?
Sorry if the question is a little vague but I have had a big hunt around and can't seem to find any further information
Thank you
Since the token is signed with a "secret" key - only the issuer can add data to it.
You can amend something to the claim set after receiving the token in your Web API - this is called claims transformation.
I have a sample of it here:
https://github.com/thinktecture/Thinktecture.IdentityModel/tree/master/samples/OWIN/AuthenticationTansformation
In essence you are writing some code that inspects the incoming token and add application specific claims to the resulting principal.
// Transform claims to application identity
app.UseClaimsTransformation(TransformClaims);
private Task<ClaimsPrincipal> TransformClaims(ClaimsPrincipal incoming)
{
if (!incoming.Identity.IsAuthenticated)
{
return Task.FromResult<ClaimsPrincipal>(incoming);
}
// Parse incoming claims - create new principal with app claims
var claims = new List<Claim>
{
new Claim(ClaimTypes.Role, "foo"),
new Claim(ClaimTypes.Role, "bar")
};
var nameId = incoming.FindFirst(ClaimTypes.NameIdentifier);
if (nameId != null)
{
claims.Add(nameId);
}
var thumbprint = incoming.FindFirst(ClaimTypes.Thumbprint);
if (thumbprint != null)
{
claims.Add(thumbprint);
}
var id = new ClaimsIdentity("Application");
id.AddClaims(claims);
return Task.FromResult<ClaimsPrincipal>(new ClaimsPrincipal(id));
}