BOT Directline Channel User Authentication - authentication

We have a web application and enabled direct client channel to communicate with the hosted BOT framework using directline secret.
Link :BOT - Directline webchat
Sample Code:
BotChat.App({
directLine: { secret: Key },
//dynamically retrieve the logged in user info in your mvc View once the user logged in and pass it on
//and pass thoes info to your bot
user: { id: '', email: '' },
bot: { id: 'testBOT' },
resize: 'detect'
}, document.getElementById("divbot"))
Here is my situration:
1) The user successfully logged in to the application and authorized using the individual account
2) How to authenticate the user in the BOT framework. The Directline secret used to authenticate the calling application. Is there any way to authrorize the authenticate the logged in user in the BOT framework securely?
Thank you

also read about getting the secret token from the key and use for communication. But not sure how to accomplish in the javascript.
It seems that you embed web chat in your MVC website, and you do not want to expose Direct Line Secret (which prevent anyone from putting your bot on their website). You can try this approach:
Create a backend service, and make request to generate Direct Line token in that service, which can avoid exposing Direct Line Secret from client side.
On your JavaScript client, you can make Ajax request to that backend service for getting Direct Line token and initiate BotChat with generated token in Ajax success callback function.
Enable CORS in your backend service to allow some origins and prevent another origins request from accessing that backend service and adding your bot in web page.
For secure your backend service, you can implement request Authentication for it.

You can exchange the key for a token that expires. Here is an mvc example: https://github.com/EricDahlvang/TokenBotExample/tree/master/TokenBotExample
string botChatSecret = ConfigurationManager.AppSettings["BotChatSecret"];
var request = new HttpRequestMessage(HttpMethod.Get, "https://webchat.botframework.com/api/tokens");
request.Headers.Add("Authorization", "BOTCONNECTOR " + botChatSecret);
using (HttpResponseMessage response = await new HttpClient().SendAsync(request))
{
     string token = await response.Content.ReadAsStringAsync();
     Token = token.Replace("\"", "");
}

Related

Social Login authentication in an API

I am using Microsoft Owin/Katana and implemented the social login authentication in a Web Forms and MVC application which is working perfectly.
I have a requirement to convert the implementation into an API (controllers). In my MVC application, I am calling this line of code inside the login controller,
HttpContext.GetOwinContext().Authentication.Challenge(properties, AuthenticationProvider);
The user is redirected to the login screen of the provider (i.e. Google, Facebook, Twitter).
I was hoping to achieve the following,
User call /api/login controller of my API and it checks the provider and then using the access key and secret key, it returns the login URL as string or JSON and the consumer of the API will redirect to that URl. This way the secret and access keys are not exposed and just the URL is returned.
The problem is that there is no way I am able to find to get the login request URL of the provider. The Challenge method is void and it just redirects the user which in case of an API is not acceptable.
I have used the following code in a Web API,
var properties = new AuthenticationProperties() { RedirectUri = this.RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
MessageRequest.GetOwinContext().Authentication.Challenge(properties, AuthenticationProvider);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
response.RequestMessage = MessageRequest;
return Task.FromResult(response);
When I call the API using Postman then it returns the whole HTML of the login page. When I call the API URL directly in the browser then it redirects to the login page correctly but I just want to return the Url of the provider and the consumer should redirect itself.
Is there any way to achieve this?

Identity Server 4, External providers and Web API

I’m in process of developing system which consists from such parts:
- Some services under gateway (Ocelot)
- Mobile client (iOS)
- Identity Server 4
Mobile client hasn’t been prepared yet, so I use Postman for emulating requests from it. My problem is implementation of Authentication with External providers, like Google. It’s my first experience of using IS 4, so I have some misunderstanding and difficulties. Excuse me, if my question is too abstract or if I miss smth obvious.
I successfully deployed IS 4 using all this tutorials and it works with Password Credentials flow in a proper way: I request IS for access token, sending user credentials, it returns token and I can successfully use it for access to my API methods.
Situation with External Providers are different. I’ve overviewed this tutorial (https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/google-logins?view=aspnetcore-3.1) and some other and add code from it to the IS project. I can successfully log in with Google, using a button on that IS4 web-page which goes with IS 4 Quickstart UI template. But no chance to work with API. As I understand in such workflow client-app should go for a token not to my IS as in example with a local user, but to the Google Auth provider. And I emulated it with Postman and got a strange access_token which has no data and it_token which contains username, email and so on. I try to use this id_token with requests to my API. The result is always 401.
Where I’m wrong? How should I build requests to API with token from Google? Or I have misunderstanding and there should be another flow: client goes to IS with specific request, IS goes to Google and then returns proper token to Client?
Here is configuration of authecation on the side of Web API app:
private void ConfigAuthentication(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.Audience = "k_smart_api";
});
}
Here is config of Google-Auth on the side of IdentityServer:
services.AddAuthentication().AddGoogle(opts => {
opts.ClientId = "My google client Id";
opts.ClientSecret = "my google client secret";
opts.SignInScheme = IdentityConstants.ExternalScheme;
opts.SaveTokens = true;
});
This is how I get Access Token:
postman exampple
The tokens you get back from Google, is only used to Authenticate the user in Identity Server. Then after Identity Server receives those tokens, it sign-in the user and create new tokens (ID+access) that are passed to your client. you should look at using the authorization code flow in your client to authenticate the user and to get the tokens. then use the access token received to access your API.
do remember that the tokens received from Google are not used to give access to your APIs.

how to protect my Web API Basic Authentication credentials

I've just started implementing Authentication in my Web API.
I want to start with Basic Authentication
I learned that i've to pass Username and Password in every request.
So, lets say i'm doing some Admin task and making API call for same like this:
$.ajax({
url: host + "homework/delete/" + $(this).data("id"),
type: 'DELETE',
headers:
{
Authorization: 'Basic ' + btoa(username + ':' + password)
},
success: function (d) {
$tr.remove();
},
error: function () {
alert("Error please try again");
}
});
So, although my username/password is in variable, but their value must be at page(source). whosoever access that page, can see those credentials.
That means, whosoever get to know the url of that page, can see the credentials.
If i put a login page, how should i check on admin page that this user is authenticated. Should i use Cookies? to set something if user is coming through login page?
To enhance security, I think there should be another approach. At first you need to authenticate to you service using username and password, and receive authentication token with limited lifetime, then you should use this token to access your services.
I think you have to choose another approach:
create a server side application with UI (PHP, Java, ...)
this application has a session management
the credentials are stored in the configuration of the server side app
the requests to the service which is secured by Basic Authentication are performed by the server app. The responses are delivered to the client
You can't hide the credentials if you are creating a client side JavaScript application. Another issue with your approach maybe this: does the secured service support CORS (cross origin resource sharing) ?

Token based authentication for both Web App and Web API using Azure AD B2C

Scenario:
Both Web application and Web API need to be authenticated and protected from the server side.
Requirement:
Web application is serving the contents for the browser and browser should be calling Web API directly (i.e. Browser to API).
Question:
Is it possible to authenticate both Web APP and the API using tokens?
Any sample code or clear direction would be highly appreciated.
Normally web applications are authenticated using cookies and APIs are authenticated using tokens.There are some sample projects available here but they are either browser to API (SPA token based) or Server side Web App calling API from server to server.
UPDATE 1
App is saving the TokenValidationParameters and used bootstrapContext.Token within the app controller to grab for server to server communication.
As per #dstrockis, I'm trying to grab the id_token from the Web App soon after the end of validation (not within the app contrller).
I'm using SecurityTokenValidated invoker in OpenIdConnectAuthenticationOptions.Notifications within the Startup class. SecurityTokenValidated receives a parameter of type SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> but I'm not sure where to find the id_token within it. Method is below.
private OpenIdConnectAuthenticationOptions CreateOptionsFromPolicy(string policy)
{
return new OpenIdConnectAuthenticationOptions
{
// For each policy, give OWIN the policy-specific metadata address, and
// set the authentication type to the id of the policy
MetadataAddress = String.Format(aadInstance, tenant, policy),
AuthenticationType = policy,
// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = clientId,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
//NEW METHOD INVOKE ************************************
//******************************************************
SecurityTokenValidated = OnSecurityTokenValidated
//******************************************************
},
Scope = "openid",
ResponseType = "id_token",
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
SaveSigninToken = true
},
};
}
//NEW METHOD ************************************
private Task OnSecurityTokenValidated(
SecurityTokenValidatedNotification<OpenIdConnectMessage,
OpenIdConnectAuthenticationOptions> arg)
{
//QUESTION ********************************************************
//How to find the just saved id_token using incoming parameter, arg
//*****************************************************************
return Task.FromResult(0);
}
UPDATE 2
Instead of SecurityTokenValidated, I tried AuthorizationCodeReceived and it's not getting called at all. As discussed here, my redirect url does have an ending slash as well.
Any Ideas?
Our ASP.NET OpenID Connect middleware which supports AAD B2C is built to rely on cookie authentication from a browser. It doesn't accept tokens in a header or anything like that for securing web pages. So I'd say if you want to serve HTML from your web app in the classic way, you need to use cookies to authenticate requests to the web app.
You can definitely get & store tokens within the browser and use those to access your web API, even if you use cookies to authenticate to the web app. There's two patterns I'd recommend:
Perform the initial login using the OpenID Connect Middleware, initiating the flow from the server side as described in the samples. Once the flow completes, the middleware will validate the resulting id_token and drop cookies in the browser for future requests. You can instruct the middleware to save the id_token for later use by using the line of code written here. You can then somehow pass that id_token down to your browser, cache it, and use it to make requests to the API.
The other pattern is the inverse. Start by initiating the login from javascript, using the single page app pattern from the B2C documentation. Cache the resulting id_tokens in the browser, and use them to make API calls. But when the login completes, you can send a request to your web app with the id_token in the body, triggering the OpenID Connect middleware to process the request and issue a session cookie. If you want to know the format of that request, I'd recommend inspecting a regular server side OpenID Connect flow.
Found the answer to my own question and adding here for the future reference.
After a successful validation, id_token can be accessed by invoking the SecurityTokenValidated notification. Code sample is below.
private Task OnSecurityTokenValidated(
SecurityTokenValidatedNotification<OpenIdConnectMessage,
OpenIdConnectAuthenticationOptions> arg)
{
//Id Token can be retrieved as below.
//**************************************
var token = arg.ProtocolMessage.IdToken;
return Task.FromResult(0);
}
However, saving this directly into a browser cookie may not be secure.

OWIN/OAuth2 3rd party login: Authentication from Client App, Authorization from Web API

I am trying to create a Web API that allows the API's clients (native mobile apps) to login using a 3rd party cloud storage provider. I'm using the following general flow from Microsoft:
Here is what I am trying to achieve:
I am using the default ASP.NET Web API Visual Studio template with external authentication, along with the OWin.Security.Providers Nuget package for Dropbox login functionality, and the existing built-in login functionality for Google (Drive) and Microsoft (OneDrive).
The issue I'm having is that the built-in functionality all seems to do the authentication and authorization as part of one flow. For example, if I set up the following in Startup.Auth.cs:
DropboxAuthenticationOptions dropboxAuthOptions = new DropboxAuthenticationOptions
{
AppKey = _dropboxAppKey,
AppSecret = _dropboxAppSecret
};
app.UseDropboxAuthentication(dropboxAuthOptions);
... and navigate to this url from my web browser:
http://<api_base_url>/api/Account/ExternalLogin?provider=Dropbox&response_type=token&client_id=self&redirect_uri=<api_base_url>
I am successfully redirected to Dropbox to login:
https://www.dropbox.com/1/oauth2/authorize?response_type=code&client_id=<id>&redirect_uri=<redirect_uri>
... and then after I grant access, am redirected back to:
http://<api_base_url>/Help#access_token=<access_token>&token_type=bearer&expires_in=1209600
... as you can see the token is part of that, so could be extracted. The problem is that the client needs to be the one navigating to Dropbox and returning the authorization code back up to the Web API, and the Web API would send the authorization code back to the third party to get the token which would then be returned to the client... as shown in the diagram above. I need the ExternalLogin action in the AccountController to somehow retrieve the Dropbox url and return that to the client (it would just be a json response), but I don't see a way to retrieve that (it just returns a ChallengeResult, and the actual Dropbox url is buried somewhere). Also, I think I need a way to separately request the token from the third party based on the authorization code.
This post seems a little similar to what I am trying to do:
Registering Web API 2 external logins from multiple API clients with OWIN Identity
... but the solution there seems to require the client to be an MVC application, which is not necessarily the case for me. I want to keep this as simple as possible on the client side, follow the flow from my diagram above, but also not reinvent the wheel (reuse as much as possible of what already exists in the OWIN/OAuth2 implementation). Ideally I don't want the client to have to reference any of the OWIN/OAuth libraries since all I really need the client to do is access an external url provided by the API (Dropbox in my example), have the user input their credentials and give permission, and send the resulting authorization code back up to the api.
Conceptually this doesn't sound that hard but I have no idea how to implement it and still use as much of the existing OAuth code as possible. Please help!
To be clear, the sample I mentioned in the link you posted CAN be used with any OAuth2 client, using any supported flow (implicit, code or custom). When communicating with your own authorization server, you can of course use the implicit flow if you want to use JS or mobile apps: you just have to build an authorization request using response_type=token and extract the access token from the URI fragment on the JS side.
http://localhost:55985/connect/authorize?client_id=myClient&redirect_uri=http%3a%2f%2flocalhost%3a56854%2f&response_type=token
For reference, here's the sample: https://github.com/aspnet-security/AspNet.Security.OpenIdConnect.Server/tree/dev/samples/Mvc/Mvc.Server
In case you'd prefer a simpler approach (that would involve no custom OAuth2 authorization server), here's another option using the OAuth2 bearer authentication middleware and implementing a custom IAuthenticationTokenProvider to manually validate the opaque token issued by Dropbox. Unlike the mentioned sample (that acts like an authorization proxy server between Dropbox and the MVC client app), the JS app is directly registered with Dropbox.
You'll have to make a request against the Dropbox profile endpoint (https://api.dropbox.com/1/account/info) with the received token to validate it and build an adequate ClaimsIdentity instance for each request received by your API. Here's a sample (but please don't use it as-is, it hasn't been tested):
public sealed class DropboxAccessTokenProvider : AuthenticationTokenProvider {
public override async Task ReceiveAsync(AuthenticationTokenReceiveContext context) {
using (var client = new HttpClient()) {
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.dropbox.com/1/account/info");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.Token);
var response = await client.SendAsync(request);
if (response.StatusCode != HttpStatusCode.OK) {
return;
}
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
var identity = new ClaimsIdentity("Dropbox");
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, payload.Value<string>("uid")));
context.SetTicket(new AuthenticationTicket(identity, new AuthenticationProperties()));
}
}
}
You can easily plug it via the AccessTokenProvider property:
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions {
AccessTokenProvider = new DropboxAccessTokenProvider()
});
It has its own downsides: it requires caching to avoid flooding the Dropbox endpoint and is not the right way to go if you want to accept tokens issued by different providers (e.g Dropbox, Microsoft, Google, Facebook).
Not to mention that if offers a very low security level: since you can't verify the audience of the access token (i.e the party the token was issued to), you can't ensure that the access token was issued to a client application you fully trust, which allows any third party developer to use his own Dropbox tokens with your API without having to request user's consent.
This is - obviously - a major security concern and that's why you SHOULD prefer the approach used in the linked sample. You can read more about confused deputy attacks on this thread: https://stackoverflow.com/a/17439317/542757.
Good luck, and don't hesitate if you still need help.