简体   繁体   中英

Alexa skill and Azure AD authentication

I am trying to build an alexa skill that connects to an enterprise app that uses Azure AD authentication. We set everything up like in this article https://blogs.msdn.microsoft.com/premier_developer/2016/12/12/amazon-alexa-skills-development-with-azure-active-directory-and-asp-net-core-1-0-web-api/ , but I am having problems with moving the token from the body of the request to the headers. The request comes through fine, I can parse it, I add the token to the header but on the test page in amazon I get this message: "There was an error calling the remote endpoint, which returned HTTP 302 : Found"

This is the code for adding the token to the headers:

 public void ConfigureAuth(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
        app.Use(async (context, next) =>
        {
            string path = string.Format(ConfigurationManager.AppSettings["ExchangeTraceDrivePath"], "AlexaRequest", DateTime.Now.ToFileTime(), "");

            var stream = context.Request.Body;
            try
            {
                using (var buffer = new MemoryStream())
                {
                    await stream.CopyToAsync(buffer);
                    var bodyBuffer = new byte[buffer.Length];
                    buffer.Position = 0L;
                    buffer.Read(bodyBuffer, 0, bodyBuffer.Length);
                    var body = Encoding.UTF8.GetString(bodyBuffer);
                    using (var sw = new StreamWriter(path))
                    {
                        sw.WriteLine(DateTime.Now.ToString() + " body: " + body);
                        sw.WriteLine("---------------------------------------------------------------------------------------------");
                        foreach (var header in context.Request.Headers)
                        {
                            sw.WriteLine(DateTime.Now.ToString() + " header key: " + header.Key);
                            foreach (var val in header.Value)
                            {
                                sw.WriteLine(DateTime.Now.ToString() + " header value: " + val);
                            }
                        }
                        sw.WriteLine("---------------------------------------------------------------------------------------------");

                        dynamic json = JObject.Parse(body);
                        sw.WriteLine(DateTime.Now.ToString() + " parsed body: " + json);
                        sw.WriteLine("---------------------------------------------------------------------------------------------");

                        if (json?.session?.user?.accessToken != null)
                        {
                            sw.WriteLine(DateTime.Now.ToString() + " access accessToken found " +
                                         json?.session?.user?.accessToken);
                            sw.WriteLine("---------------------------------------------------------------------------------------------");

                            context.Request.Headers.Add("Authorization",
                                new string[] { string.Format("Bearer {0}", json?.session?.user?.accessToken) });
                            foreach (var header in context.Request.Headers)
                            {
                                sw.WriteLine(DateTime.Now.ToString() + " header key: " + header.Key);
                                foreach (var val in header.Value)
                                {
                                    sw.WriteLine(DateTime.Now.ToString() + " header value: " + val);
                                }
                            }
                            sw.WriteLine("---------------------------------------------------------------------------------------------");
                        }
                        buffer.Position = 0L;
                        context.Request.Body = buffer;
                    }
                }
            }
            catch
            {
            }
            finally
            {
                await next.Invoke();
                // Restore the original stream.
                context.Request.Body = stream;
            }

        });

        //ExpireTimeSpan and SlidinExpiration only work when  UseTokenLifetime = false
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            // This is NOT ASP.NET Session Timeout (that should be set to same value in web.config)
            // This is the expiration on the cookie that holds the Azure AD token
            ExpireTimeSpan = TimeSpan.FromMinutes(Convert.ToDouble(expirationTimeSpan)),

            // Set SlidingExpiration=true to instruct the middleware to re-issue a new cookie
            // with a new expiration time any time it processes a request which is more than
            // halfway through the expiration window.
            SlidingExpiration = true,

            Provider = new CookieAuthenticationProvider
            {
                // This method is called every time the cookie is authenticated, which
                // is every time a request is made to the web app
                OnValidateIdentity = CookieAuthNotification.OnValidateIdentity
            }
        });

        app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {
                ClientId = clientId,
                Authority = authority,
                UseTokenLifetime = false,
                /*
                * Skipping the Home Realm Discovery Page in Azure AD
                * http://www.cloudidentity.com/blog/2014/11/17/skipping-the-home-realm-discovery-page-in-azure-ad/
                */
                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    RedirectToIdentityProvider = OpenIdConnectNotification.RedirectToIdentityProvider,
                    MessageReceived = OpenIdConnectNotification.MessageReceived,
                    SecurityTokenReceived = OpenIdConnectNotification.SecurityTokenReceived,
                    SecurityTokenValidated = OpenIdConnectNotification.SecurityTokenValidated,
                    AuthorizationCodeReceived = OpenIdConnectNotification.AuthorizationCodeReceived,
                    AuthenticationFailed = OpenIdConnectNotification.AuthenticationFailed
                },

            });
    }

I ended up creating a separate middleware to move the token from the body to the header

 public class AlexaJWTMiddleware : OwinMiddleware
{
    private readonly OwinMiddleware _next;

    public AlexaJWTMiddleware(OwinMiddleware next) : base(next)
    {
        _next = next;
    }


    public override Task Invoke(IOwinContext context)
    {
        var stream = context.Request.Body;
        if (context.Request.Headers.ContainsKey("SignatureCertChainUrl")
            && context.Request.Headers["SignatureCertChainUrl"]
                .Contains("https://s3.amazonaws.com/echo.api/echo-api-cert-4.pem")
            && !context.Request.Headers.ContainsKey("Authorization"))
        {

            try
            {
                using (var buffer = new MemoryStream())
                {
                    stream.CopyToAsync(buffer);
                    var bodyBuffer = new byte[buffer.Length];
                    buffer.Position = 0L;
                    buffer.Read(bodyBuffer, 0, bodyBuffer.Length);
                    var body = Encoding.UTF8.GetString(bodyBuffer);

                    dynamic json = JObject.Parse(body);
                    if (json?.session?.user?.accessToken != null)
                    {

                        context.Request.Headers.Add("Authorization",
                            new string[] { string.Format("Bearer {0}", json?.session?.user?.accessToken) });
                    }
                    buffer.Position = 0L;
                    context.Request.Body = buffer;
                }
            }
            catch
            {
            }
            finally
            {
                // Restore the original stream.
                context.Request.Body = stream;
            }
        }
        else
        {
            return _next.Invoke(context);

        }
        return _next.Invoke(context);

    }
}

and then adding jwt authentication besides the openId one

  public void ConfigureAuth(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
        app.Use(typeof(AlexaJWTMiddleware));
        app.UseWindowsAzureActiveDirectoryBearerAuthentication(
            new WindowsAzureActiveDirectoryBearerAuthenticationOptions
            {
                Tenant = domain,
                TokenValidationParameters = new TokenValidationParameters
                {
                    ValidAudience = ConfigurationManager.AppSettings["ida:AppIdUri"]
                },
                AuthenticationType = "OAuth2Bearer",
            });

        //ExpireTimeSpan and SlidinExpiration only work when  UseTokenLifetime = false
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            // This is NOT ASP.NET Session Timeout (that should be set to same value in web.config)
            // This is the expiration on the cookie that holds the Azure AD token
            ExpireTimeSpan = TimeSpan.FromMinutes(Convert.ToDouble(expirationTimeSpan)),

            // Set SlidingExpiration=true to instruct the middleware to re-issue a new cookie
            // with a new expiration time any time it processes a request which is more than
            // halfway through the expiration window.
            SlidingExpiration = true,

            Provider = new CookieAuthenticationProvider
            {
                // This method is called every time the cookie is authenticated, which
                // is every time a request is made to the web app
                OnValidateIdentity = CookieAuthNotification.OnValidateIdentity
            }
        });

        app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {
                ClientId = clientId,
                Authority = authority,
                UseTokenLifetime = false,
                /*
                * Skipping the Home Realm Discovery Page in Azure AD
                * http://www.cloudidentity.com/blog/2014/11/17/skipping-the-home-realm-discovery-page-in-azure-ad/
                */
                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    RedirectToIdentityProvider = OpenIdConnectNotification.RedirectToIdentityProvider,
                    MessageReceived = OpenIdConnectNotification.MessageReceived,
                    SecurityTokenReceived = OpenIdConnectNotification.SecurityTokenReceived,
                    SecurityTokenValidated = OpenIdConnectNotification.SecurityTokenValidated,
                    AuthorizationCodeReceived = OpenIdConnectNotification.AuthorizationCodeReceived,
                    AuthenticationFailed = OpenIdConnectNotification.AuthenticationFailed
                },

            });
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM