Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AspNetCore UseOpenIdConnectAuthentication

I am using Azure AD B2C to authenticate in an AspNetCore RC2 MVC application, this partially works in that when I navigate to an action that requires authentication I am redirected to the B2C login page accordingly. When I successfully login I am correctly redirected to my application page (and I can see the id_token field appropriately provided in the query parameters). Unfortunately the pipeline authentication middleware does not seem to correctly processing the redirect query parameters as it immediately redirects me to the login page. Can anyone advise?

The code I am using is below:

public static void UseOAuth(this IApplicationBuilder app)
{
    // By default, all middleware are passive/not automatic. Making cookie middleware automatic so that it acts on all the messages. 
    app.UseCookieAuthentication( new CookieAuthenticationOptions{ AutomaticAuthenticate = true, CookieSecure = CookieSecureOption.Never });

    app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions { 
        ClientId = B2CAuthentication.ClientId,
        ResponseType = OpenIdConnectResponseTypes.IdToken,
        Authority = string.Format(CultureInfo.InvariantCulture, B2CAuthentication.AadInstance, B2CAuthentication.PortalTenant, string.Empty, string.Empty),
        AuthenticationScheme = "Cookies",
        Events = new OpenIdConnectEvents
        {
            OnAuthenticationFailed = OnAuthenticationFailed,
            OnRedirectToIdentityProvider = OnRedirectToIdentityProvider,
            OnAuthorizationCodeReceived = OnAuthorizationCodeReceived,
            OnTokenResponseReceived = OnTokenResponseReceived,
            OnTokenValidated = OnTokenValidated,
            OnTicketReceived = OnTicketReceived,
            OnMessageReceived = OnMessageReceived,
            OnRedirectToIdentityProviderForSignOut = OnRedirectToIdentityProviderForSignOut,
            OnRemoteFailure = OnRemoteFailure,
            OnUserInformationReceived = OnUserInformationReceived
        },
        // The PolicyConfigurationManager takes care of getting the correct Azure AD authentication 
        // endpoints from the OpenID Connect metadata endpoint.  It is included in the PolicyAuthHelpers folder. 
        ConfigurationManager = new PolicyConfigurationManager(
            string.Format(CultureInfo.InvariantCulture, B2CAuthentication.AadInstance, B2CAuthentication.PortalTenant, "/v2.0", "/" + OpenIdProviderMetadataNames.Discovery),
            new string[] { B2CAuthentication.ResetPolicy, B2CAuthentication.CommonPolicy, B2CAuthentication.SignInPolicy })

    });
}

private static Task OnUserInformationReceived(UserInformationReceivedContext arg)
{
    ...Never called...
}

private static Task OnRemoteFailure(FailureContext arg)
{
    ...Never called...
}

private static Task OnRedirectToIdentityProviderForSignOut(RedirectContext arg)
{
    ...Never called...
}

private static Task OnMessageReceived(MessageReceivedContext arg)
{
    ...Never called...
}

private static Task OnTicketReceived(TicketReceivedContext arg)
{
    ...Never called...
}

private static Task OnTokenValidated(TokenValidatedContext arg)
{
    ...Never called...
}

private static Task OnTokenResponseReceived(TokenResponseReceivedContext arg)
{
    ...Never called...
}

private static Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext arg)
{
    ...Never called...
}

private static async Task OnRedirectToIdentityProvider(RedirectContext context)
{
    PolicyConfigurationManager mgr = (PolicyConfigurationManager)context.Options.ConfigurationManager;
    if (context.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
    {
        OpenIdConnectConfiguration config = await mgr.GetConfigurationByPolicyAsync(CancellationToken.None, B2CAuthentication.CommonPolicy);
        context.ProtocolMessage.IssuerAddress = config.EndSessionEndpoint;
    }
    else
    {
        OpenIdConnectConfiguration config = await mgr.GetConfigurationByPolicyAsync(CancellationToken.None, B2CAuthentication.CommonPolicy);
        context.ProtocolMessage.IssuerAddress = config.AuthorizationEndpoint;
        context.ProtocolMessage.RedirectUri = "http://localhost:8080/Portal/";
        context.ProtocolMessage.ResponseType = OpenIdConnectResponseTypes.IdToken;
        context.ProtocolMessage.ResponseMode = OpenIdConnectResponseModes.Query;
    }
}

private static Task OnAuthenticationFailed(AuthenticationFailedContext context)
{
    context.HandleResponse();
    context.Response.Redirect("/Home/Error?message=" + context.Exception.Message);
    return Task.FromResult(0);
}
like image 344
Slicc Avatar asked Dec 25 '22 05:12

Slicc


2 Answers

I've managed to get this to work by doing the following:

Fundamentally I think the use of the CallbackPath, the change to AuthenticationScheme and the change of ResponseMode to FormPost all contributed to the fix.

public static void UseOAuth(this IApplicationBuilder app)
{
    // By default, all middleware are passive/not automatic. Making cookie middleware automatic so that it acts on all the messages. 
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AutomaticAuthenticate = true,
        CookieName = "MyCookieName",
        CookieSecure = CookieSecureOption.Never,
        AuthenticationScheme =  "Cookies"
    });

    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = new Dictionary<string, string>();

    app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions { 
        AutomaticAuthenticate = true,
        Authority = string.Format(CultureInfo.InvariantCulture, B2CAuthentication.AadInstance, B2CAuthentication.PortalTenant, string.Empty, string.Empty),
        ClientId = B2CAuthentication.ClientId,
        ResponseType = OpenIdConnectResponseTypes.IdToken,
        AuthenticationScheme = "oidc",
        ResponseMode = OpenIdConnectResponseModes.FormPost,
        CallbackPath = "/",
        Scope = { "openid" },
        Events = new OpenIdConnectEvents
        {
            OnAuthenticationFailed = OnAuthenticationFailed,
            OnRedirectToIdentityProvider = OnRedirectToIdentityProvider,
            OnTokenValidated = OnTokenValidated,
            OnRemoteFailure = OnRemoteFailure
        },
        // The PolicyConfigurationManager takes care of getting the correct Azure AD authentication 
        // endpoints from the OpenID Connect metadata endpoint.  It is included in the PolicyAuthHelpers folder. 
        ConfigurationManager = new PolicyConfigurationManager(
            string.Format(CultureInfo.InvariantCulture, B2CAuthentication.AadInstance, B2CAuthentication.PortalTenant, "/v2.0", "/" + OpenIdProviderMetadataNames.Discovery),
            new string[] { B2CAuthentication.ResetPolicy, B2CAuthentication.CommonPolicy, B2CAuthentication.SignInPolicy })

    });
}
like image 142
Slicc Avatar answered Feb 13 '23 15:02

Slicc


Using

ResponseType = OpenIdConnectResponseType.Code 

inside

app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
    ...
});

seems to make the middleware prompt for the CodeReceived event.

Also add Microsoft.AspNetCore.Mvc.Formatters.Xml to project.json to get away from the Could not load file or assembly System.Private.DataContractSerialization error

like image 20
abhijoseph Avatar answered Feb 13 '23 16:02

abhijoseph