Read Custom request Header in OWIN Authentication - ajax

I am trying to provide extra/custom authentication to MS Bot-framework project using OWIN other than default authentication from MS using app id/pwd. Yes the Bot is in-fact an api i tagged Webapi too. I added OWIN startup class and provided middleware to perform OAUTH-2 implementation to validate JWT.
As MS Bot directline calls have a default Bearer token to be passed as Authorization header key,i given custom provider to accept JWT from Bot state. Please note my bot is surfaced in a Web app which will generate a auth token which will be setted in Bot state against unique user id, so i am in need of this user id value to retrieve the token from Bot state. So the best possible way i can think of is to intercept all ajax calls from my Webchat Bot control to add a custom header as "x-user-id", which i will read from my owin middleware request header.
But it was not succeeding as i am not getting the header value in OWIN, which i am passing in ajax calls. But when i checked in Chrome, this header is being sent. I am confused on what could be the issue.
Ajax Interceptor
if (window.XMLHttpRequest && !(window.ActiveXObject)) {
(function (send) {
XMLHttpRequest.prototype.send = function (data) {
this.setRequestHeader('x-user-id', '123456789');
send.call(this, data);
};
})(XMLHttpRequest.prototype.send);
}
AppBuilder Configuration
public void Configuration(IAppBuilder app)
{
var policy = new CorsPolicy()
{
AllowAnyHeader = true,
AllowAnyMethod = true,
AllowAnyOrigin = true,
SupportsCredentials = true
};
policy.ExposedHeaders.Add("x-user-id");
app.UseCors(new CorsOptions()
{
PolicyProvider = new CorsPolicyProvider
{
PolicyResolver = context => Task.FromResult(policy)
}
});
app.Map("/api", ctx =>
{
ctx.UseEsoAccessTokenValidation(new EsoAccessTokenOptions
{
AccessTokenKey = "AccessToken",
ChannelId = "webchat",
Scopes = new string[] { "read", "write" }
});
ctx.UseWebApi(WebApiConfig.Register());
});
}
Code to Read Header:
private static async Task<string> GetAccessToken(OAuthRequestTokenContext context, EsoAccessTokenOptions options)
{
string accesstoken = string.Empty;
var request = context.Request;
if (request.Headers.ContainsKey("x-user-id"))
{
userid = request.Headers.Get("x-user-id");
}
}
Chrome Network Screenshot
Please help me understand what i am doing wrong here?

Need to realize that, the requests always pass through the corresponding channels (connectors) and not everything that you add as part of headers will be passed to your bot. Hence we need to pass such data only through supported mechanisms. as Eric mentioned, once such way is leveraging channelData. Anything that you add as part of channelData will pass thru the channel and reach the bot. Hence recommend you to try the same.
Example 1:
{
"type": "conversationUpdate",
"membersAdded": [],
"from": {
"id": "test",
"name": "test"
},
"serviceUrl": "https://directline.botframework.com",
"channelData":{ "userId": "test"}
}
Example 2:
{
"type": "message",
"text": "whats your name",
"from": {
"id": "user1",
"name": "user1"
},
"channelData": {
"userId": "test1234"
}
}`
Hope this helps.

I think the Direct Line strips headers as it translates and transfers messages to your bot. However, you could intercept all messages (like you've done already) and add the x-user-id as custom channel data to each message. Here is an example of custom channel data: https://blog.botframework.com/2017/03/28/Custom-Channel-Data#c-implementation

Related

YouTube Data API: add a subscription

I'm using YouTube's V3 Data API to add a subscription to a channel. This occurs on a Wordpress installation.
I added Google APIs (for oauth) on Wordpress theme functions:
wp_enqueue_script( 'googleapi', 'https://apis.google.com/js/client.js?onload=googleApiClientReady', array(), '1.0.0', true );
I added in the same way the oauth javascript file, which is the first one here: https://developers.google.com/youtube/v3/code_samples/javascript.
Following this guide(https://developers.google.com/youtube/v3/docs/subscriptions/insert (Apps Script)), I extended the OAuth js with the addSubscription method.
Google Client API seems to be loaded and working as it calls correctly googleApiClientReady on the oauth javascript.
So, this is how the subscription is being inserted:
OAUTH JAVASCRIPT
... ... ...
// After the API loads
function handleAPILoaded() {
addSubscription();
}
function addSubscription() {
// Replace this channel ID with the channel ID you want to subscribe to
var channelId = 'this is filled with the channel ID';
var resource = {
snippet: {
resourceId: {
kind: 'youtube#channel',
channelId: channelId
}
}
};
try {
var response = YouTube.Subscriptions.insert(resource, 'snippet');
jQuery('#success').show();
} catch (e) {
if(e.message.match('subscriptionDuplicate')) {
jQuery('#success').show();
} else {
jQuery('#fail').show();
alert("Please send us a mail () with the following: ERROR: " + e.message);
}
}
So, the first error comes with
YouTube.Subscriptions.insert(resource, 'snippet')
It says YouTube is not defined. I replaced it with:
gapi.client.youtube.subscriptions.insert(resource, 'snippet');
And that error went away. When checking response, as the subscription isn't completed, this is what I get
{"wc":1,"hg":{"Ph":null,"hg":{"path":"/youtube/v3/subscriptions","method":"POST","params":{},"headers":{},"body":"snippet","root":"https://www.googleapis.com"},"wc":"auto"}}
So, I would like to know what's happening on that POST request and what's the solution to this.
I can post the full OAuth file, but it's just as in the example, plus that addSubscription method at the end.
Okay, I got it working, the problem was on the POST request. Here is the full method working:
// Subscribes the authorized user to the channel specified
function addSubscription(channelSub) {
var resource = {
part: 'id,snippet',
snippet: {
resourceId: {
kind: 'youtube#channel',
channelId: channelSub
}
}
};
var request = gapi.client.youtube.subscriptions.insert(resource);
request.execute(function (response) {
var result = response.result;
if (result) {
// alert("Subscription completed");
}
} else {
// alert("Subscripion failed");
// ...
}
});
}
Also make sure to load Google Apps API (in fact without it the authorize/login button won't work) and jQuery.
Any chance you can post everything that made this work...all the JS entire auth.js save for your private keys, im working on this exact problem.

How to add claims to access token get from IdentityServer3 using resource owner flow with javascript client

I use the resource owner flow with IdentityServer3 and send get token request to identity server token endpoint with username and password in javascript as below:
function getToken() {
var uid = document.getElementById("username").value;
var pwd = document.getElementById("password").value;
var xhr = new XMLHttpRequest();
xhr.onload = function (e) {
console.log(xhr.status);
console.log(xhr.response);
var response_data = JSON.parse(xhr.response);
if (xhr.status === 200 && response_data.access_token) {
getUserInfo(response_data.access_token);
getValue(response_data.access_token);
}
}
xhr.open("POST", tokenUrl);
var data = {
username: uid,
password: pwd,
grant_type: "password",
scope: "openid profile roles",
client_id: 'client_id'
};
var body = "";
for (var key in data) {
if (body.length) {
body += "&";
}
body += key + "=";
body += encodeURIComponent(data[key]);
}
xhr.setRequestHeader("Authorization", "Basic " + btoa(client_id + ":" + client_secret));
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(body);
}
The access token is returned from identity server and user is authenticated. Then I use this token to send request to my Web Api.
The problem is that when I check if the user is assigned a role, I find the claim doesn't exist.
[Authorize]
// GET api/values
public IEnumerable<string> Get()
{
var id = RequestContext.Principal as ClaimsPrincipal;
bool geek = id.HasClaim("role", "Geek"); // false here
bool asset_mgr = id.HasClaim("role", "asset_manager"); // false here
return new string[] { "value1", "value2" };
}
Here is how the client is defined in identity server.
new Client
{
ClientName = "Client",
ClientId = "client_id",
Flow = Flows.ResourceOwner,
RequireConsent = false,
AllowRememberConsent = false,
AllowedScopes = new List<string>
{
"openid",
"profile",
"roles",
"sampleApi"
},
AbsoluteRefreshTokenLifetime = 86400,
SlidingRefreshTokenLifetime = 43200,
RefreshTokenUsage = TokenUsage.OneTimeOnly,
RefreshTokenExpiration = TokenExpiration.Sliding,
ClientSecrets = new List<Secret>
{
new Secret("4C701024-0770-4794-B93D-52B5EB6487A0".Sha256())
},
},
and this is how the user is defined:
new InMemoryUser
{
Username = "bob",
Password = "secret",
Subject = "1",
Claims = new[]
{
new Claim(Constants.ClaimTypes.GivenName, "Bob"),
new Claim(Constants.ClaimTypes.FamilyName, "Smith"),
new Claim(Constants.ClaimTypes.Role, "Geek"),
new Claim(Constants.ClaimTypes.Role, "Foo")
}
}
How can I add claims to the access_token in this case? Thanks a lot!
I have just spent a while figuring this out myself. #leastprivilege's comment on Yang's answer had the clue, this answer is just expanding on it.
It's all down to how the oAuth and OIDC specs evolved, it's not an artefact of IdentityServer (which is awesome).
Firstly, here is a fairly decent discussion of the differences between identity tokens and access tokens: https://github.com/IdentityServer/IdentityServer3/issues/2015 which is worth a read.
With Resource Owner flow, like you are doing, you will always get an Access Token. By default and per the spec, you shouldn't include claims in that token (see the above link for why). But, in practice, it is very nice when you can; it saves you extra effort on both client and server.
What Leastprivilege is referring to is that you need to create a scope, something like this:
new Scope
{
Name = "member",
DisplayName = "member",
Type = ScopeType.Resource,
Claims = new List<ScopeClaim>
{
new ScopeClaim("role"),
new ScopeClaim(Constants.ClaimTypes.Name),
new ScopeClaim(Constants.ClaimTypes.Email)
},
IncludeAllClaimsForUser = true
}
And then you need to request that scope when you ask for the token. I.e. your line
scope: "openid profile roles", should change to scope: "member", (well, I say that - scopes play a dual role here, as far as I can see - they are also a form of control, i.e. the client is asking for certain scopes and can be rejected if it is not allowed those but that is another topic).
Note the important line that eluded me for a while, which is Type = ScopeType.Resource (because Access Tokens are about controlling access to resources). This means it will apply to Access Tokens and the specified claims will be included in the token (I think, possibly, against spec but wonderfully).
Finally, in my example I have included both some specific claims as well as IncludeAllClaimsForUser which is obviously silly, but just wanted to show you some options.
I find I can achieve this by replacing the default IClaimsProvider of IdentityServerServiceFactory.
The cusomized IClaimsProvider is as below:
public class MyClaimsProvider : DefaultClaimsProvider
{
public MaccapClaimsProvider(IUserService users) : base(users)
{
}
public override Task<IEnumerable<Claim>> GetAccessTokenClaimsAsync(ClaimsPrincipal subject, Client client, IEnumerable<Scope> scopes, ValidatedRequest request)
{
var baseclaims = base.GetAccessTokenClaimsAsync(subject, client, scopes, request);
var claims = new List<Claim>();
if (subject.Identity.Name == "bob")
{
claims.Add(new Claim("role", "super_user"));
claims.Add(new Claim("role", "asset_manager"));
}
claims.AddRange(baseclaims.Result);
return Task.FromResult(claims.AsEnumerable());
}
public override Task<IEnumerable<Claim>> GetIdentityTokenClaimsAsync(ClaimsPrincipal subject, Client client, IEnumerable<Scope> scopes, bool includeAllIdentityClaims, ValidatedRequest request)
{
var rst = base.GetIdentityTokenClaimsAsync(subject, client, scopes, includeAllIdentityClaims, request);
return rst;
}
}
Then, replace the IClaimsProvider like this:
// custom claims provider
factory.ClaimsProvider = new Registration<IClaimsProvider>(typeof(MyClaimsProvider));
The result is that, when the request for access token is sent to token endpoint the claims are added to the access_token.
Not only that I tried other methods, I tried all possible combinations of scopes etc. All I could read in the access token was "scope", "scope name", for Resource Flow there were no claims I have added period.
I had to do all this
Add custom UserServiceBase and override AuthenticateLocalAsync since I have username/password there and I need both to fetch things from the database
Add claims that I need in the same function (this on itself will not add claim to Access Token, however you will able to read them in various ClaimsPrincipal parameters around)
Add custom DefaultClaimsProvider and override GetAccessTokenClaimsAsync where ClaimsPrincipal subject contains the claims I previously set, I just take them out and put again into ølist of claims for the result.
I guess this last step might be done overriding GetProfileDataAsync in the custom UserServiceBase, but the above just worked so I did not want to bother.
The general problem is not how to set claims, it is where you populate them. You have to override something somewhere.
This here worked for me since I needed data from a database, someone else should populate claims elsewhere. But they are not going to magically appear just because you nicely set Scopes and Claims Identity Server configurations.
Most of the answers say not a word about where to set the claim values properly. In each particular override you have done, the passed parameters, when they have claims, in the function are attached to identity or access token.
Just take care of that and all will be fine.

slack api rtm direct message

I'm using a node package: slack-client to interact with the api at slack. Now with or without using slack-client how do I send a direct message from my bot to a user I want to specify? Here's what have so far with a plain socket connection:
var WebSocket = require('ws')
,ws2 = new WebSocket(myURL); //from rtm start
ws2.on('open', function() {
ws2.send({
"id": 333,
"type": "message",
"channel": "#user1", //User I want to send to
"text": "HEY!!!!"
});
});
ws2.on('message', function(message) {
console.log('received: %s', message);
});
I was hoping that message would go directly to me from the bot but nothing. I get a reply of type hello though? The send details above I got on another post about this but it doesn't work for me. The message Id was one I created.
Ok so when calling the rtm.start via the web api, you would get a list of DM's that would be open for various users otherwise you can easily just open an im with im.open. I'm using the node package slack-client as mentioned in my question so you can do this:
//name of user your bot wants to send a msg to.
var userTest = slack.getUserByName('user1');
slack.openDM(userTest.id, function(res)
{
TestMsg(res.channel.id, 'some other msg');//test function I'm using
});
Next is the TestMsg function:
function TestMsg(userChannelId, msg)
{
request.post({url: 'https://slack.com/api/chat.postMessage',
form: { token: "xxxx-yourbot-token",channel: userChannelId,text: msg ,username: "yourBotNamehere", as_user: false}
}, function(error, response, body){
console.log(response.body);
});
}
I couldn't get it to work yet using the websockets send method but I suppose the api of postMessage will do for now as you can post richly formatted messages with postMessage. Hope this helps someone

Dart Language: Authentication and session control (shelf_auth)

I'm developing a Dart application that will need authentication and session control. I'm trying shelf_auth to do that, but the examples doesn't seem to work or, more likely, I'm not implementing them the right way.
In short, this is what I want to happen:
An user opens the application on the browser.
The user enters the login information (login and password), which are POSTED to the server.
If the provided information is valid, the application generates a session code that is passed to the client and stored on the DB (server-side). This code will be sent with every transaction to the server-side.
The package shelf_auth has some examples, but I don't know which one to follow. So my question is: how could I do that with shelf_auth? I'm not asking for anyone to code this for me, but just to point me to the right direction.
EDIT: The example that I was trying out was this: example_with_login_and_jwt_session.dart. Seems that it's lacking CORS headers (this question helped me fixing it) and, even providing valid information, it responds "Unauthorized".
This is how I'm POSTING the information:
import "dart:html";
void main() {
Map _queryParameters = {
"username": "fred",
"password": "blah"
};
var _button = querySelector("#login_button");
_button.onClick.listen((MouseEvent e) {
e.preventDefault();
var requisition = new HttpRequest();
Uri uri = new Uri(path: "http://localhost:8080/login", queryParameters: _queryParameters);
requisition.open("POST", uri.toString());
requisition.setRequestHeader("content-type", "application/x-www-form-urlencoded");
requisition.onLoadEnd.listen((_) {
print(requisition.response.toString());
});
requisition.send();
});
}
I got it working with this client code
import "dart:html";
void main() {
Map _queryParameters = {"username": "fred", "password": "blah"};
var _button = querySelector("#login_button");
_button.onClick.listen((MouseEvent e) async {
e.preventDefault();
var requisition = new HttpRequest();
Uri uri = new Uri(
path: "http://localhost:8080/login/");
requisition.onLoadEnd.listen((_) {
print(requisition.response.toString());
});
HttpRequest request = await HttpRequest.postFormData(
"http://localhost:8080/login/", _queryParameters
//,withCredentials: true
);
print(request.response);
});
}
The example server expects the credentials in the body instead of query parameters and I set withCredentials: true so authentication cookies are sent with the request. Worked without withCredentials.

Passing and verifying the OWIN Bearer token in Query String in WebAPI

Short Version:
I need to pass and verify the OWIN bearing token as a query parameter rather than in the request header.
How do I then get the method to authorized based on that token string?
Background:
I want to call a webapi method to download a file as a stream (and never want the user to download it from a known file location).
I can't get this to work if I also need to set a custom Request header i.e. the bearer token.
I should be able to pass the token in the query string - but don't know how to get that token to then authenticate the user.
Do I need to filter? Do I need a special claim etc?
Does the webapi method need to include "access_token" as one of the function parameters?
For completeness, here's another neat solution.
Extract:
app.Use(async (context, next) =>
{
if (context.Request.QueryString.HasValue)
{
if (string.IsNullOrWhiteSpace(context.Request.Headers.Get("Authorization")))
{
var queryString = HttpUtility.ParseQueryString(context.Request.QueryString.Value);
string token = queryString.Get("access_token");
if (!string.IsNullOrWhiteSpace(token))
{
context.Request.Headers.Add("Authorization", new[] { string.Format("Bearer {0}", token) });
}
}
}
await next.Invoke();
});
I wrote about how that works here:
http://leastprivilege.com/2013/10/31/retrieving-bearer-tokens-from-alternative-locations-in-katanaowin/
or do it like this
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = IdentityConfig.Authority,
RequiredScopes = new[] { "api" },
TokenProvider = new OAuthBearerAuthenticationProvider
{
OnRequestToken = ctx =>
{
if (String.IsNullOrWhiteSpace(ctx.Token) && ctx.Request.QueryString.HasValue)
{
NameValueCollection parsedQuery = HttpUtility.ParseQueryString(ctx.Request.QueryString.Value);
ctx.Token = parsedQuery["access_token"];
}
return Task.FromResult(0);
}
}
});

Resources