Giter Site home page Giter Site logo

microsoftarchive / botauth Goto Github PK

View Code? Open in Web Editor NEW
72.0 72.0 78.0 366 KB

Authentication middleware for the botframework

License: MIT License

JavaScript 29.59% TypeScript 14.37% C# 52.28% ASP 0.28% HTML 3.36% Dockerfile 0.12%
authentication-middleware bot bot-framework botbuilder-framework botbuilder-sdk oauth2

botauth's People

Contributors

bnookala avatar mattdot avatar navzam avatar richdizz avatar ritazh avatar tayzlor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

botauth's Issues

Using Bot Framework State Service?

Is this project using the Bot Framework State Service? Looking at the code, it seems like the AuthDialog and CallbackController are using it to pass back and forth the authentication values. Since the State Service is now deprecated, won't that cause problem using this code in production? Or am I mistaken?

SampleGenericOAuth2Bot not working

I am trying to use the sample SampleGenericOAuth2Bot in my Chatbot, and implemented it by basically copying and pasting the code into my login dialog, but when running and after choosing the provider I get this exception:

Could not load type 'System.IdentityModel.Tokens.ISecurityTokenValidator' from assembly 'System.IdentityModel.Tokens.Jwt, Version=5.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.

My packages look like this, am I missing anything?

  <package id="Autofac" version="4.6.2" targetFramework="net46" />
  <package id="Autofac.WebApi2" version="4.1.0" targetFramework="net46" />
  <package id="AutoMapper" version="6.2.2" targetFramework="net46" />
  <package id="BotAuth" version="3.9.0-alpha" targetFramework="net46" />
  <package id="BotAuth.GenericOAuth2" version="3.9.0-alpha" targetFramework="net46" />
  <package id="Chronic.Signed" version="0.3.2" targetFramework="net46" />
  <package id="EntityFramework" version="6.2.0" targetFramework="net46" />
  <package id="Microsoft.AspNet.WebApi" version="5.2.4-preview1" targetFramework="net46" />
  <package id="Microsoft.AspNet.WebApi.Client" version="5.2.4-preview1" targetFramework="net46" />
  <package id="Microsoft.AspNet.WebApi.Core" version="5.2.4-preview1" targetFramework="net46" />
  <package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.4-preview1" targetFramework="net46" />
  <package id="Microsoft.Azure.DocumentDB" version="1.19.1" targetFramework="net46" />
  <package id="Microsoft.Azure.KeyVault.Core" version="2.0.5-preview" targetFramework="net46" />
  <package id="Microsoft.Bot.Builder" version="3.12.2.4" targetFramework="net46" />
  <package id="Microsoft.Bot.Builder.Azure" version="3.2.5" targetFramework="net46" />
  <package id="Microsoft.Bot.Builder.CognitiveServices" version="1.1.2" targetFramework="net46" />
  <package id="Microsoft.Bot.Builder.History" version="3.12.2.4" targetFramework="net46" />
  <package id="Microsoft.Bot.Connector" version="3.12.2.4" targetFramework="net46" />
  <package id="Microsoft.Data.Edm" version="5.8.3" targetFramework="net46" />
  <package id="Microsoft.Data.OData" version="5.8.3" targetFramework="net46" />
  <package id="Microsoft.Data.Services.Client" version="5.8.3" targetFramework="net46" />
  <package id="Microsoft.IdentityModel.Logging" version="5.2.0-preview2-41113220915" targetFramework="net46" />
  <package id="Microsoft.IdentityModel.Protocols" version="5.2.0-preview2-41113220915" targetFramework="net46" />
  <package id="Microsoft.IdentityModel.Protocols.OpenIdConnect" version="2.1.5" targetFramework="net46" />
  <package id="Microsoft.IdentityModel.Tokens" version="5.2.0-preview2-41113220915" targetFramework="net46" />
  <package id="Microsoft.Owin" version="4.0.0-preview1" targetFramework="net46" />
  <package id="Microsoft.Owin.Security" version="4.0.0-preview1" targetFramework="net46" />
  <package id="Microsoft.Owin.Security.Jwt" version="4.0.0-preview1" targetFramework="net46" />
  <package id="Microsoft.Owin.Security.OAuth" version="4.0.0-preview1" targetFramework="net46" />
  <package id="Microsoft.Rest.ClientRuntime" version="2.3.10" targetFramework="net46" />
  <package id="Microsoft.WindowsAzure.ConfigurationManager" version="3.2.3" targetFramework="net46" />
  <package id="Newtonsoft.Json" version="11.0.1-beta3" targetFramework="net46" />
  <package id="OAuth2" version="0.8.40" targetFramework="net46" />
  <package id="Owin" version="1.0" targetFramework="net46" />
  <package id="QnAMakerDialog" version="1.1.1" targetFramework="net46" />
  <package id="RestSharp" version="106.3.0-alpha0002" targetFramework="net46" />
  <package id="System.IdentityModel.Tokens.Jwt" version="5.2.0-preview2-41113220915" targetFramework="net46" />
  <package id="System.Spatial" version="5.8.3" targetFramework="net46" />
  <package id="System.ValueTuple" version="4.4.0" targetFramework="net46" />
  <package id="WindowsAzure.Storage" version="8.7.0" targetFramework="net46" />

SigninCard doesn't render within Teams on iPhone

Teams on the iPhone shows a blank message when authorization is required - the card is visible on the desktop platform, just doesn't render on the phone. Swapping the card type generated in AuthDialog.PromptToLogin to a HeroCard instead of a SigninCard does seem to be a workround - the card and login button render.

Account linking example with facebook

Hey there,
Cool module! I'm looking at implementing an account linking example using a facebook messenger bot. I notice the example you've got around this is incomplete.

Have you had any success with trying to implement account linking with OAuth end to end?

Doesn't support setting B2C policy

I'm not sure if this lib is intended to work with Azure AD B2C but currently it does not support using it with a policy specified

It seems the underlying passport-azure-ad lib does support B2C but the button url generated does not include a policy parameter or a means in the options to specify one which means that the passport-azure-ad throws an error - see oidcstrategy.js

There needs to be some way to pass the policy as part of the options so that the button url can be created correctly

Use html page for showing magic code

Provide a default html page or view

Allow the html page to be overridden by the API consumer.
{
staticDirectory : __dirname + "/public",
magicPage : "magic.html"
}

Documentation

It is not clear on how to set up the botAuth with Azure AD.
The documentation for AADv2 does not specify the following.

  1. how to set up AAD application and get the app id and app password.
  2. how to set up permissions.
  3. how to get the user name/email from AD itself...
    even the authbot repo does not use botAuth library.
    what to give as login url and logout url etc...

Is it possible to provide a detailed setup documentation or update the documentation?

Port to Asp .Net Core 2?

When will this be available to ASP Net Core 2.0?
To get it working on ASP .NET Core 2.0 I've made a copy of Callbak controller with a few changes.
Changes I've made are HttpResponseMessage changed to IActionResult , modified routing to Microsoft.AspNetCore.Mvc.Route.
Then I used Dependecy Injection to get MicrosoftAppCredentials and created new ConnectorClient
ConnectorClient client = new ConnectorClient(new Uri(message.ServiceUrl), credentials);
insted of using this await Conversation.ResumeAsync(conversationRef, message);, and returned
new ContentResult(); Example:

[Microsoft.AspNetCore.Mvc.Route("[controller]")]
    public class CallbackController : Controller
    {
        private static RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
        private static readonly uint MaxWriteAttempts = 5;
        private readonly MicrosoftAppCredentials credentials;

        public CallbackController(MicrosoftAppCredentials credentials)
        {
            this.credentials = credentials;
        }

        [Microsoft.AspNetCore.Mvc.HttpGet]
        public async Task<IActionResult> Callback([FromQuery] string code, [FromQuery] string state, CancellationToken cancellationToken)
        {
            try
            {
                // Use the state parameter to get correct IAuthProvider and ResumptionCookie
                var decoded = Encoding.UTF8.GetString(HttpServerUtility.UrlTokenDecode(state));
                var queryString = HttpUtility.ParseQueryString(decoded);
                var assembly = Assembly.Load(queryString["providerassembly"]);
                var type = assembly.GetType(queryString["providertype"]);
                var providername = queryString["providername"];
                IAuthProvider authProvider;
                if (type.GetConstructor(new Type[] { typeof(string) }) != null)
                    authProvider = (IAuthProvider)Activator.CreateInstance(type, providername);
                else
                    authProvider = (IAuthProvider)Activator.CreateInstance(type);

                var conversationRef = UrlToken.Decode<ConversationReference>(queryString["conversationRef"]);

                Activity message = conversationRef.GetPostToBotMessage();
                using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message))
                {
                    // Get the UserData from the original conversation
                    IBotDataStore<BotData> stateStore = scope.Resolve<IBotDataStore<BotData>>();
                    var key = Address.FromActivity(message);
                    var userData = await stateStore.LoadAsync(key, BotStoreType.BotUserData, CancellationToken.None);                   
                    // Get Access Token using authorization code
                    var authOptions = userData.GetProperty<AuthenticationOptions>($"{authProvider.Name}{ContextConstants.AuthOptions}");
                    var token = await authProvider.GetTokenByAuthCodeAsync(authOptions, code);

                    // Generate magic number and attempt to write to userdata
                    int magicNumber = GenerateRandomNumber();
                    bool writeSuccessful = false;
                    uint writeAttempts = 0;
                    while (!writeSuccessful && writeAttempts++ < MaxWriteAttempts)
                    {
                        try
                        {
                            userData.SetProperty($"{authProvider.Name}{ContextConstants.AuthResultKey}", token);
                            if (authOptions.UseMagicNumber)
                            {
                                userData.SetProperty($"{authProvider.Name}{ContextConstants.MagicNumberKey}", magicNumber);
                                userData.SetProperty($"{authProvider.Name}{ContextConstants.MagicNumberValidated}", "false");
                            }
                            await stateStore.SaveAsync(key, BotStoreType.BotUserData, userData, CancellationToken.None);
                            await stateStore.FlushAsync(key, CancellationToken.None);
                            writeSuccessful = true;
                        }
                        catch (Exception)
                        {
                            writeSuccessful = false;
                        }
                    }
                    var resp = new ContentResult();
                    if (!writeSuccessful)
                    {
                        message.Text = String.Empty; // fail the login process if we can't write UserData
                     ConnectorClient client = new ConnectorClient(new Uri(message.ServiceUrl), credentials);
                        await client.Conversations.SendToConversationAsync(message);
                        resp.ContentType = @"text/html";
                        resp.Content = "<html><body>Could not log you in at this time, please try again later</body></html>";
                    }
                    else
                    {
                        message.Text = String.Empty;
                        ConnectorClient client = new ConnectorClient(new Uri(message.ServiceUrl), credentials);
                        await client.Conversations.SendToConversationAsync(message);
                        resp.ContentType = @"text/html";
                        resp.Content = $"<html><body>Almost done! Please copy this number and paste it back to your chat so your authentication can complete:<br/> <h1>{magicNumber}</h1>.</body></html>";

                    }
                    return resp;
                }
            }
            catch (Exception ex)
            {
                // Callback is called with no pending message as a result the login flow cannot be resumed.             
                return BadRequest();
            }
        }
        private int GenerateRandomNumber()
        {
            int number = 0;
            byte[] randomNumber = new byte[1];
            do
            {
                rngCsp.GetBytes(randomNumber);
                var digit = randomNumber[0] % 10;
                number = number * 10 + digit;
            } while (number.ToString().Length < 6);
            return number;
        }

    }

Logout not shown in examples and clicking the sign out link throws error

The logout functionality is not shown in any samples as compared to that of AuthBot.
On seeing the code, this function does the logout

public async Task Logout(AuthenticationOptions authOptions, IDialogContext context)
        {
            context.UserData.RemoveValue($"{this.Name}{ContextConstants.AuthResultKey}");
            context.UserData.RemoveValue($"{this.Name}{ContextConstants.MagicNumberKey}");
            context.UserData.RemoveValue($"{this.Name}{ContextConstants.MagicNumberValidated}");
            string signoutURl = "https://login.microsoftonline.com/common/oauth2/logout?post_logout_redirect_uri=" + System.Net.WebUtility.UrlEncode(authOptions.RedirectUrl);
            await context.PostAsync($"In order to finish the sign out, please click at this [link]({signoutURl}).");
        }

But on calling it, and clicking the link replied back, I get the following error message in browser:
image

What should be value of YOUR_DOMAIN_GOES_HERE ?

This might feel like very basic question but I'm confused on what exactly the value of this variable would be. Will it be azure app service or some Sprint boot localhost ? According to the documentation here I used azure endpoint of bot service in AADv2 apps.dev portal as well as in apps.js but I get following error.

{"code":"ResourceNotFound","message":"/botauth/aadv2 does not exist"}

TypeError: Cannot read property 'resumption' of undefined

After logging in into Facebook and getting redirected I get this error:

TypeError: Cannot read property 'resumption' of undefined
at node_modules/botauth/lib/index.js:156:56
at Layer.handle [as handle_request] (node_modules/express/lib/router/layer.js:95:5)
at next (node_modules/express/lib/router/route.js:137:13)
at node_modules/botauth/lib/resumption.js:35:13
at Layer.handle [as handle_request] (node_modules/express/lib/router/layer.js:95:5)
at next (node_modules/express/lib/router/route.js:137:13)
at complete (node_modules/passport/lib/middleware/authenticate.js:250:13)
at node_modules/passport/lib/middleware/authenticate.js:257:15
at pass (node_modules/passport/lib/authenticator.js:421:14)
at Authenticator.transformAuthInfo (node_modules/passport/lib/authenticator.js:443:5)

Asking for my situation

Hey I have 2 questions want to ask
Question 1: If I want authenticate my system via OAuth2, when user fill there loginID and password, the system will generate token, so which the stragety passport in this case
Question 2 : Could we use this solution when user communicate the bot via Mobile because as I imagine, the popup will be opened on mobile browse and Its may be hard to return token.
Thanks.

Working with Express JS

I raised an issue a few days ago about the bot not being compatible with Express JS. I found that the issue is that the cookie set in the redirect gets lost on the callback (my example is using the Facebook strategy).
I forked the repo to work in a solution. What I ended up doing was creating dynamic routes for the botauth endpoints, using the bot user id. For that to work I had to change the behaviour to set the callbackUrl dynamically, which is possible. With that id I was able to store the address in a closure map, that then I was able to retrieve in the callback endpoint.
So this worked for me (using express and Facebook strategy). But not sure if it would work for others. I know Restify is not that great handling dynamic routes.
If anybody is having the same issue, Im happy to share my solution (or just look at my fork)

Thanks for the hard work on this library, its awesome!

ContextConstants not matching up with UserData Schema

I may be missing something in my implementation, however, I've found what appears to be a mismatch the schema in IDialogContext for UserData and BotAuth.ContextConstants. The properties in ConstantContext seem to missing the precursor for the provider. For example:

BotAuth.ContextConstants.AuthResult returns _authMagicNumberValidated
Microsoft.Bot.Builder.Dialogs.IBotDataBag returns MSALAuthProvider_authMagicNumberValidated

image

Authentication fails in Teams 1:1 chat

I think the problem is in the region of line 81 of botauth/dialogs/authdialog.cs, which strips off the Teams at-mention in a group chat. For 1:1 chats, there's no but there is extra whitespace around the number. I think a fix might be to always Trim() the message text, whether there's an or not.

Feature Request: Multiple authentication options

Hi there, loving BotAuth, makes account linking a (relative) breeze! Just wondering if firstly, there are any plans to make it possible to use multiple authentication options in the dialog, which could be displayed using a carousel or something?

Also, can I set custom authentication text for the card, button and dialog?

Bug reading response of undefined args

on index.ts line 188 you are trying to do:
originalArgs: args.response
However, if this is the first step in the waterfall Im getting args is undefined. The solution would be something on the lines of:
args ? args.response : {}

I forked the repo to try to work on a solution. But I'm having trouble building the library. Could you provide a development process description?
I installed typings to download you type dependencies. But when I run gulp build Im getting some errors (that might be related to getting different versions of the installed types, or not. Here are the errors:
10:03:14] Using gulpfile botauth/gulpfile.js [10:03:14] Starting 'build'... .../botauth/node_modules/typescript/lib/lib.es6.d.ts(4842,11): error TS2428: All declar ations of 'WeakMap' must have identical type parameters. .../botauth/node_modules/typescript/lib/lib.es6.d.ts(5049,11): error TS2428: All declar ations of 'WeakMap' must have identical type parameters. .../botauth/node_modules/typescript/lib/lib.es6.d.ts(5825,11): error TS2428: All declar ations of 'WeakMap' must have identical type parameters. .../botauth/typings/globals/node/index.d.ts(103,11): error TS2428: All declarations of 'WeakMap' must have identical type parameters. .../botauth/typings/globals/node/index.d.ts(113,25): error TS2344: Type 'K' does not sa tisfy the constraint 'object'. .../botauth/node_modules/typescript/lib/lib.es6.d.ts(4842,11): error TS2428: All declar ations of 'WeakMap' must have identical type parameters. .../botauth/node_modules/typescript/lib/lib.es6.d.ts(5049,11): error TS2428: All declar ations of 'WeakMap' must have identical type parameters. .../botauth/node_modules/typescript/lib/lib.es6.d.ts(5825,11): error TS2428: All declar ations of 'WeakMap' must have identical type parameters. .../botauth/typings/globals/node/index.d.ts(103,11): error TS2428: All declarations of 'WeakMap' must have identical type parameters. .../botauth/typings/globals/node/index.d.ts(113,25): error TS2344: Type 'K' does not sa tisfy the constraint 'object'. [10:03:16] TypeScript: 5 semantic errors [10:03:16] TypeScript: 5 emit errors [10:03:16] TypeScript: emit failed [10:03:16] Finished 'build' after 1.7 s

[Authority] field ignored - Authentication Fails for enterprise tenant with BotAuth.AADv2

I am building a bot App that connects to my enterprise O365 tenant. When using BotAuth.AADv2 to authenticate I get the a response from Azure that my app can't use the common endpoint and I should use my tenant endpoint.

I specified my tenant "Authority" endpoint in the "AuthenticationOptions" to be my tenant:
https://login.microsoftonline.com/{tenantId}/oauth2/token but the authentication still fails

I traced the code and the problem is not passing the "Authority" parameter to the "ConfidentialClientApplication" in the MSALAuthProvider

I updated the following two lines to make it work:
Line 57-58:

 ConfidentialClientApplication client = new ConfidentialClientApplication(authOptions.ClientId,
 **authOptions.Authority**, redirectUri.ToString(), new ClientCredential(authOptions.ClientSecret), 
tokenCache, null);

Line 67-68:

ConfidentialClientApplication client = new ConfidentialClientApplication(authOptions.ClientId, 
**authOptions.Authority**, authOptions.RedirectUrl, new ClientCredential(authOptions.ClientSecret), 
tokenCache, null);

I can submit a pull request if you like.

ExpressJS incompatibility, error 500: "TypeError: Cannot read property 'resumption' of undefined"

Hey, i am running into issues #14 and #16 again.

It seems botauth in the current version is still incompatible with the express web server.

I am trying to connect to AADv2 and get a 500 server error when opening the AADv2 callback url https://somebot.eu.ngrok.io/botauth/aadv2/callback?code=OAQABAAIAAADX8GCi6Js6SK82TsD2Pb7r35MlsfImkvXuDJc9P6ppQOjkQYpAPSZhTklOid3MNSrt6D559VicvOWluX0ebmgkzYP3J3ZE3taFUvmWusaBP2WBHR7xhhzv_HiG3SeqyjNQK0bEp4D8OB5dO1E1nNYRr8prZEfcp7g8lAZn9vHoFoQv3NEJHEA9Vgn8Lu1FR7beCN3QoygJPhNA8R_WFqYUjILr9bNSTyPPrpcfh2RJT0xKAjiv-QgRZRoCNsgMwbmbUCEkDjdudC5qo0yxS44-BHgiRVkHNEi7J7jJXHn7Or2O2tg1tjZ5Kx2kENP134LQOXaYSz_W6-jniG2oHJNS4CjSjlwuFgjCPGt5P3E8H2CiGNv93TrTTR9DGgG2JHOekFMSn2j0STHnBMI5zRGYGrG0rzSbrQB8EmlaDhX_BcGlqh7WATSwkZdU0HKfASeWZt4nOoGmTdv5Pk54SPIpKOcMaPNUDI4jvxjqvBCphiK9Ao9lunlslkP6HqzAlS2sfsZO-gnuXFNLYKQ_n4bUJ-v1oolVCpgLFiy_PjCdJSAqHqBoJI9nE_GqUJJqxo1c1Gqtld-IVHZ7iOwLi39Ue8KbqTB7WGUCl6c3LHVqYyg-RwVeu2vV69Tz5PuJd1jI9NDG4ZkVYJJfjidycCDqmfvEhaLgaDKh2nf3gt7ISo_J9tmh_uCKWn4uUQRAGyhVLzJYgb8viQA1-86G9ocK6q9VOVXD2XJrgHClDxFOaiAA&state=sIVYMo3WaRNJ06TxPCqIYnFKcltosthu&session_state=b94b095c-6ab2-44b1-a4de-35965bcca1e5.

Express Stacktrace:

at /projects/somebot/node_modules/botauth/lib/index.js:156:57
at Layer.handle [as handle_request] (/projects/somebot/node_modules/express/lib/router/layer.js:95:5)
at next (/projects/somebot/node_modules/express/lib/router/route.js:131:13)
at /projects/somebot/node_modules/botauth/lib/resumption.js:35:13
at Layer.handle [as handle_request] (/projects/somebot/node_modules/express/lib/router/layer.js:95:5)
at next (/projects/somebot/node_modules/express/lib/router/route.js:131:13)
at complete (/projects/somebot/node_modules/passport/lib/middleware/authenticate.js:250:13)
at /projects/somebot/node_modules/passport/lib/middleware/authenticate.js:257:15
at pass (/projects/somebot/node_modules/passport/lib/authenticator.js:421:14)
at Authenticator.transformAuthInfo (/projects/somebot/node_modules/passport/lib/authenticator.js:443:5)
TypeError: Cannot read property 'resumption' of undefined

The in #14 suggested botauth fork does not work, either.
Is there any workaround available besides switching to restify?

Regards

botAuth and Auth0 and Invalid authorization code

Hello,

I'm trying to use botauth to connect to an Auth0 client using the passport-auth0 strategy to authorize access to my api that sits behind an API gateway.

Using the Bot Framework Emulator, and commenting out lines 36-38 of index.js so I can use a an http callback address (perhaps that is my issue), I am able to enter my credentials with Auth0 , click login and the bot logs my profile

However.... after authenticating in the browser, the bot redirects to http://WEB_HOSTNAME:PORT/botauth/auth0?state=... hangs there for a bit, then redirects to http://WEB_HOSTNAME:PORT/botauth/auth0/callback?code=... with the response:

{"message":"Invalid authorization code"}

and I never get my bot conversation resumed in the emulator.

I wrote up a quick test based on the project's pintrest example it it helps anyone work out what I am doing wrong...any help would be appreciated...

Notes:

  • Same behavior whether or not I use server.use(clientSessions({ secret: BOTAUTH_SECRET }));
  • Tested against Auth0 Client of type Regular Web Application
    ** Token Endpoint Authentication Method : POST
    ** Allowed Callback URLs : configured with proper callback URL (http) in this case
    ** Allowed Logout URLs : hadn't got that far
  • Tested against Auth0 Client of type Non Interactive Client
    ** Token Endpoint Authentication Method : POST
    ** Allowed Callback URLs : configured with proper callback URL (http) in this case
    ** Allowed Logout URLs : hadn't got that far

Thanks in advance.

"use strict";

const restify = require("restify");
const builder = require("botbuilder");
const envx = require("envx");
const botauth = require("botauth");
const clientSessions = require("client-sessions");

const passport = require("passport");
const Auth0Strategy = require('passport-auth0')

//contextual service information
const WEBSITE_HOSTNAME = envx("WEB_HOSTNAME");
const PORT = envx("PORT", 3998);

//bot application identity
const MICROSOFT_APP_ID = envx("MICROSOFT_APP_ID");
const MICROSOFT_APP_PASSWORD = envx("MICROSOFT_APP_PASSWORD");

//oauth details for pinterest
const AUTH0_DOMAIN= envx("AUTH0_DOMAIN");
const AUTH0_CLIENT_ID = envx("AUTH0_CLIENT_ID");
const AUTH0_CLIENT_SECRET = envx("AUTH0_CLIENT_SECRET");

//encryption key for saved state
const BOTAUTH_SECRET = envx("BOTAUTH_SECRET");

var server = restify.createServer();
server.use(restify.bodyParser());
server.use(restify.queryParser());
server.use(clientSessions({ secret: BOTAUTH_SECRET }));

// Create chat connector with bot's Microsoft app identity
var connector = new builder.ChatConnector({
    appId: MICROSOFT_APP_ID,
    appPassword: MICROSOFT_APP_PASSWORD
});

// Create bot builder client and connect it to restify server
var bot = new builder.UniversalBot(connector);
server.post('/api/messages', connector.listen());

// Initialize with the strategies we want to use
var ba = new botauth.BotAuthenticator(server, bot, { baseUrl : "http://" + WEBSITE_HOSTNAME, secret : BOTAUTH_SECRET })
    .provider("auth0", (options) => {
        return new Auth0Strategy({
            domain: AUTH0_DOMAIN,
            clientID: AUTH0_CLIENT_ID,
            clientSecret: AUTH0_CLIENT_SECRET,
            scope: 'openid profile email identities',
            //scope: ['openid', 'profile', 'email', 'identities'],
            //scope: [ 'read_public', 'read_relationships' ],
            callbackURL: options.callbackURL
            //callbackURL: options.callbackURL
        }, (accessToken, refreshToken, profile, done) => {
            profile = profile || {};
            profile.accessToken = accessToken;
            profile.refreshToken = refreshToken;
            profile.provider = "auth0"; // workaround
            console.log('===========')
            console.log(profile)
            console.log('===========')
            return done(null, profile);
        });
    });



/**
 * Just a page to make sure the server is running
 */
server.get("/", (req, res) => {
    res.send("auth0");
});

//=========================================================
// Bot Dialogs
//=========================================================
bot.dialog('/', new builder.IntentDialog()
    .matches(/^hello/i, "/hello")
    .matches(/^data/i, "/data")
    .matches(/^logout/i, "/logout")
    .onDefault((session, args) => {
        session.endDialog("I didn't understand that.  Try saying 'data'.");
    })
);

bot.dialog("/hello", (session, args) => {
    session.endDialog("Hello. I can help you get information from my service.  Try saying 'data'.");
});

bot.dialog("/data", [].concat(
    ba.authenticate("auth0"),
    function(session, results) {
        // ---------  code omitted
        // call my api behind gateway using jwt token... return data.
        // ---------  code omitted
        
       // let's just log the user if we get here for now
        var user = ba.profile(session, "auth0");
        console.log(user)
        
));

bot.dialog("/logout", [
    (session, args, next) => {
        builder.Prompts.confirm(session, "are you sure you want to logout");
    }, (session, args) => {
        if(args.response) {
            ba.logout(session, "auth0");
            session.endDialog("you've been logged out.");
        } else {
            session.endDialog("you're still logged in");
        }
    }
]);

//start the server
server.listen(PORT, function () {
   console.log('%s listening to %s', server.name, server.url);
});

verify the user token in top dialogues

Hello -

In my scenario, I',m using the authentication in the root dialogue and once the user successfully logged in I'm forwarding the user to the other dialogues. Once the users are in other dialogue, how can we verify that the user is still valid, is there a helper utility to validate the user token once we are on other dialogues?

Regards,
Sowmyan

Does not gel with Bot Service created via Azure Portal

I am trying to authorise a Bot created via Azure Portal with AAD, however the instructions here do not work.

Specifically:

  1. "Click on 'Authentication/Authorization' under the Settings section". This option is not shown for the service. Option is there for general app services, but not the bot.
  2. WEBSITE_HOSTNAME. When trying to save with this App Setting, an error is returned "Invalid or not allowed environment key name",

BotAuth is obsolete?

BotBuilder since release 3.15.1.0 supports generic OAuth flows and AADv1/v2 authentication methods out of the box. There are examples for each auth flow in official documentation. Integrated flows are also capable of making all the verifications in the background. This project even being presented on Build 2018 seem to be abandoned.

@mattdot should it be discontinued and marked as such?

(node.js) - Aadv2 sample is not working

Unless I'm missing something the Azure AD v2 sample does not work.

I have followed all the steps described in the readme.md. The bot does respond properly to the inputs I give, then when I initiate the signin dialogue it renders the card with the oauth sign in button. However when I then try to log in it does redirect to the callbackpage (the html page in the public folder).

As far as how I've understood it, it should then show a "magic code" which I need to send to the bot to finish the authentication process. However this code

is empty, there is nothing shown at all.

Things I've tried to send to the bot which didn't work at all:

  • use the value of the "ARRAffinity" cookie
  • use the state value
  • use the code value of the callback url path

What am I supposed to send to the bot after succesfully being redirected by the bot?

Also if I set the hostname to the botservice url the redirect to the callback 404's and signin never is able to reach the /code page. Am I supposed to do extra configuration for this or is this intended?

My bot is hosted using the Azure botservice.

Feature Request: Magic code-less authentication flow

Thanks a lot for the examples was battling with this for many days and had to switch to Microsoft bot builder because it's the only one where I could find a working example of account linking.
I'm trying to build a flow similar to the one used in foxsy bot https://www.messenger.com/t/foxsybot
Anyone knows how to use the default Authorization from facebook dialogue where it's asking for permission instead of the magic code? Was kind of taken aback when I didn't saw that appearing :D Didn't know it was possible to make oauth request without it.

error 500: "TypeError: Cannot read property 'resumption' of undefined" when authenticating with Facebook

Hello,

I am currently able talk with my bot using Microsoft Bot Framework (Webapp & Messenger), it works, with different dialogs etc. I use ngrok endpoint on my Dev Messenger app and Microsoft Bot Framework bot. On my webapp (without bot), I implemented passport-facebook authentification to my MongoDB and it works well, I can connect choosing between local or Facebook authentication, and get back data.

What I want is now is one of these solutions:

  1. From the bot client, my users have to login using Facebook channel (or locally on my webapp, using bot <iframe> webapp channel, or on localhost test with ngrok...) to my remote API, so with the bot I can get back some data from my MongoDB, as I do on my webapp. I need at least my Facebook ID.

  2. Connect transparently to my remote API with Facebook authentication, so when users talk, they are directly connected, no webapp modal or anything, I can directly use their information from my remote MongoDB, and let's go.

I tried to implement 1) solution, because I really don't know how to do the second solution, which would be amazing for users.

But when I try to authenticate with Facebook using botauth, I have this error on:

TypeError: Cannot read property 'resumption' of undefined
    at /Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/botauth/lib/index.js:156:56
    at Layer.handle [as handle_request] (/Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/express/lib/router/layer.js:95:5)
    at next (/Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/express/lib/router/route.js:137:13)
    at /Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/botauth/lib/resumption.js:35:13
    at Layer.handle [as handle_request] (/Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/express/lib/router/layer.js:95:5)
    at next (/Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/express/lib/router/route.js:137:13)
    at complete (/Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/passport/lib/middleware/authenticate.js:250:13)
    at /Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/passport/lib/middleware/authenticate.js:257:15
    at pass (/Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/passport/lib/authenticator.js:421:14)
    at Authenticator.transformAuthInfo (/Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/passport/lib/authenticator.js:443:5)
    at /Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/passport/lib/middleware/authenticate.js:254:22
    at IncomingMessage.req.login.req.logIn (/Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/passport/lib/http/request.js:63:13)
    at Strategy.strategy.success (/Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/passport/lib/middleware/authenticate.js:235:13)
    at verified (/Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/passport-oauth2/lib/strategy.js:177:20)
    at Strategy.FacebookStrategy [as _verify] (/Users/pierre/Desktop/Work/GitHub/alenvi-bot/app.js:82:12)
    at /Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/passport-oauth2/lib/strategy.js:193:24
    at /Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/passport-facebook/lib/strategy.js:181:5
    at passBackControl (/Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/oauth/lib/oauth2.js:134:9)
    at IncomingMessage.<anonymous> (/Users/pierre/Desktop/Work/GitHub/alenvi-bot/node_modules/oauth/lib/oauth2.js:157:7)
    at emitNone (events.js:91:20)
    at IncomingMessage.emit (events.js:185:7)
    at endReadableNT (_stream_readable.js:974:12)

(picture here: https://puu.sh/vQPNb/917c710ff8.png )

I nearly copy / pasted your Facebook example, just removing Luis, and using Express instead of Restify.
I start by saying 'profile', then I click on 'connect', and then I have the error I described above. I have to cancel after.
(picture: https://puu.sh/vQQ7A/1a37693288.png)

Here is a bit of code:

var ba = new botauth.BotAuthenticator(app, bot, {
  baseUrl: "https://" + WEBSITE_HOSTNAME, secret: BOTAUTH_SECRET
}).provider("facebook", (options) => {
  return new FacebookStrategy({
    clientID: process.env.FACEBOOK_APP_ID || FACEBOOK_APP_ID,
    clientSecret: process.env.FACEBOOK_APP_SECRET || FACEBOOK_APP_SECRET,
    callbackURL: options.callbackURL,
    profileFields: ['id', 'emails', 'name', 'photos']
  }, (accessToken, refreshToken, profile, done) => {
    profile.accessToken = accessToken;
    profile.refreshToken = refreshToken;
    console.log(profile);
    return done(null, profile);
  });
});

console.log(profile); code displays well all what I need in my console. So it seems to nearly work !

But:

bot.dialog("/profile", [].concat(
  ba.authenticate("facebook"),
  function(session, results) {
    //get the facebook profile
    var user = ba.profile(session, "facebook");
    //var user = results.response;
    console.log(user);
    console.log("hello");
  }
));

console.log(user); and console.log("hello"); don't log anything (because it's in response of the callback I suppose)

Can you help me ? I feel really lost, after an entire day on this.

Do not hesitate to tell me if you need something more.

Thank you,

Pierre

Add capability to run behind pass through API gateway.

I have to run my bots in a kubernetes cluster behind a software gateway (not on a public host).

This software gateway simply proxies requests for my bot, it accepts requests at https://bot.domain.com and relays them to http://mybot. This works well enough for messages.

The problem comes in when we add botauth: the options.basePath value is used for both route creation and as well as the generation of the callbackUrl and authUrl. So this means that our service endpoints can't be different from our auth and callback urls. Note that I can already do this with the code page by using options.successRedirect.

I'm sure this is by design (as the documentation always says your bot has to be running on a public host), but I'd find it useful if you added another botAuth option, say options.baseServicePath that can be used to define the routes on the server that do not necessarily match the externally exposed routes.

So for instance:

  let options = {
    baseUrl : 'https://bot.domain.com',
    basePath: '/botauth',
    baseServicePath:  '/mybot',
    //...,
    successRedirect: `https://bot.domain.com/botauth/providerId/code`
  };

  let botAuth = new botauth.BotAuthenticator(server, bot, options);
  //...
  server.get(`/${server.name}/botauth/providerId/code`, serveStatic({'directory': path.join(__dirname, 'public'), 'file': 'code.html'}));

Would give me the following external URLS:

Which could easily be mapped in my gateway to my service routes:

Obviously, issue 61 not withstanding, I think this could easily be done with:

  // botauth/lib/index.js

  // .. line 40
  if (!this.options.baseServicePath) {
    this.options.baseServiceUrl = this.options.baseUrl
  }
 // update these three lines. (around line 59)
  this.server.get(`/${this.options.baseServicePath}/:providerId`, this.options.resumption.persistHandler(), this.passport_redirect());
  this.server.get(`/${this.options.baseServicePath}/:providerId/callback`, this.passport_callback(), this.options.resumption.restoreHandler(), this.credential_callback());
  this.server.post(`/${this.options.baseServicePath}/:providerId/callback`, this.passport_callback(), this.options.resumption.restoreHandler(), this.credential_callback());

In any event, happy to contribute if that would work.

Custom Provider Example

Hey Dudes,

Just wondering if in the case of authenticating against an Oauth2 based authentication mechanism (ie. Ping Federate), I need to implement a custom provider? I tried using the GenericOAuth2 provider, but can't seem to add a client to the authorizationroot object (https://github.com/MicrosoftDX/botauth/blob/master/CSharp/BotAuth.GenericOAuth2/GenericOAuth2Provider.cs, line 63).

Do I need to implement my own provider? Or just add to the list of genericOathProviders in the object above?

Teams 1:1 signin issue

The signin option does not work in 1:1 / private conversations. It works from within a teams channel. The signin card displays, but am not able to click the sign in button.

Using JWT with botauth?

Is it possible to use the jwt passport strategy with botauth? Is it that only the defined strategies in the readme file work or we can use any strategy?

Where are the nugets?

This issues comes in two parts:

  • There is a section about nugets but I didn't found a nuspec file yet
  • I searched for "BotAuth.AADv2" and installed but I'm trying to use the "AuthenticationOptions" class and found out that the core isn't included as dependency as is told on Nuget section

[BUG] AADV2 sample responds Unauthorized on GET /botauth/aadv2/callback?code=[code]

I've just pulled the example and am attempting to get the auth flow working.

When redirected back to my site with the code issued by the v2 endpoint I'm presented with an Unauthorized message and an HTTP 401 status code.

Changes made to cloned code:

I've set WEBSITE_HOST to localhost:3978 and used the App Id and App Password I generated on apps.dev.microsoft.com

I have changed my local copy of botauth on line 36 so that an http address will work and set allowHttpForRedirectUrl: true so that I don't have to mess around getting an SSL cert or ngrok working locally.

magic.html error 404

Hi -

Not sure if this is expected here so want to check. During the authentication flow, after the successful authentication redirected to http://localhost:3984/magic.html#152210 with code suffixed. But getting the 404 error and for end user its confusing. So want to check am I missing anything ?

image

Regards,
Sowmyan

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.