Session fixation vulnerability in the Auth0 ASP.NET and OWIN SDKs

Recently, I received a mail from Auth0 asking me if I was interested in joining them. I had used Auth0 many times in the past but I had never taken the time to look at their OSS SDKs. This mail was a good opportunity to change that.

When doing so, I discovered that both their ASP.NET 4.x and OWIN/Katana SDKs were unfortunately prone to "session fixation", which is a form of cross-site request forgery allowing to force a victim to log in under an attacker's account.

Since it's a quite frequent vulnerability, here's a quick overview of what causes it and how you can concretly exploit it.

Auth0 was already aware of this issue internally and decided to switch to the official OWIN OpenID Connect middleware developed by Microsoft, which is not prone to this class of attack.

If you need to migrate, Auth0 has prepared a migration guide listing the steps required to replace Auth0-ASPNET-Owin by Microsoft's OpenID Connect middleware.

What do session fixation attacks consist in?

The OAuth 2.0 threat model RFC – a must read for anyone dealing with OAuth 2.0 and OpenID Connect – gives an excellent definition of this threat and its practical implications:

Cross-site request forgery (CSRF) is a web-based attack whereby HTTP requests are transmitted from a user that the web site trusts or has authenticated (e.g., via HTTP redirects or HTML forms). CSRF attacks on OAuth approvals can allow an attacker to obtain authorization to OAuth protected resources without the consent of the user. This attack works against the redirect URI used in the authorization "code" flow.

An attacker could authorize an authorization "code" to their own protected resources on an authorization server. He then aborts the redirect flow back to the client on his device and tricks the victim into executing the redirect back to the client. The client receives the redirect, fetches the token(s) from the authorization server, and associates the victim's client session with the resources accessible using the token.

Impact: the user accesses resources on behalf of the attacker. The effective impact depends on the type of resource accessed. For example, the user may upload private items to an attacker's resources. Or, when using OAuth in 3rd-party login scenarios, the user may associate his client account with the attacker's identity at the external Identity Provider. In this way, the attacker could easily access the victim's data at the client by logging in from another device with his credentials at the external Identity Provider.

Usually, this (generally underestimated) threat is mitigated by correlating the authorization response with the authorization request: typically, by generating an unguessable value before redirecting the user to the Identity Provider and validating it before sending the token request.

Unfortunately, this kind of check is not made by the Auth0 ASP.NET 4.x and OWIN SDKs, making them vulnerable to this class of attack.

Read more

Implementing advanced scenarios using the new OpenIddict RC3 events model

Prior to OpenIddict RC3, the events model used by the OpenID Connect server middleware (i.e the OIDC server framework behind OpenIddict) was deliberately not accessible due to the nature of OpenIddict: being initially designed for non-experts, exposing such a powerful API – that allows altering the way OpenID Connect requests are processed – didn't seem like a good idea at first sight.

With time, the core audience of OpenIddict has evolved a bit to not only include beginners but also developers who were already familiar with OAuth/OpenID Connect or had used OAuthAuthorizationServerMiddleware or ASOS in the past. For them, the fact OpenIddict didn't allow them to take control of the request processing pipeline was often a blocker. With the introduction of OpenIddict RC3, we're changing that.

Using these advanced APIs is not recommended if you're not familiar with the OAuth/OpenID Connect specifications or with the events model used by the OpenID Connect server middleware. If you're not sure whether you should use these APIs, don't hesitate to reach us on Gitter or on GitHub.

Introducing event handlers

The events model is structured around IOpenIddictServerEventHandler<TEvent> and IOpenIddictValidationEventHandler<TEvent>. These 2 interfaces represent handlers that are invoked every time an event of type TEvent is triggered by the OpenIddict server or validation handlers.

At the time of writing, OpenIddict exposes 44 server events – grouped into the OpenIddictServerEvents static class to make them easier to find – and 5 validation events, exposed under OpenIddictValidationEvents.

Each event represents a specific moment in the request processing pipeline (e.g the moment the OpenIddict server determines whether the request is an OpenID Connect request it should handle, the moment it extracts it, handles it or returns a response).

Multiple handlers of the same type can be registered: they will be sequentially invoked in the same order as the one used to register them. As soon as a handler calls a method that indicates the request should no longer be processed (e.g HandleResponse() or SkipHandler()), OpenIddict will stop invoking the handlers and the next ones will be automatically ignored.

For security reasons, the custom handlers will be invoked by OpenIddict after its own validation routines. If a request is rejected by OpenIddict, your own handlers won't be invoked.

Creating and registering a custom event handler

Creating an event handler is straightforward: pick the event you need and add a class that implements either IOpenIddictServerEventHandler<TEvent> (for a handler that receives events triggered by the OpenIddict server services) or IOpenIddictValidationEventHandler<TEvent> (for a handler that receives events triggered by the OpenIddict token validation services):

For instance, to return custom metadata in the discovery document, you'll need to implement IOpenIddictServerEventHandler<OpenIddictServerEvents.HandleConfigurationRequest>:

1
2
3
4
5
6
7
8
9
public class MyEventHandler : IOpenIddictServerEventHandler<OpenIddictServerEvents.HandleConfigurationRequest>
{
public Task HandleAsync(OpenIddictServerEvents.HandleConfigurationRequest notification, CancellationToken cancellationToken)
{
notification.Context.Metadata["company_name"] = "Contoso";

return Task.CompletedTask;
}
}

To register it, use options.AddEventHandler<TEvent, THandler>() (by default, the handler is registered as a scoped service):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void ConfigureServices(IServiceCollection services)
{
services.AddOpenIddict()

// Register the OpenIddict core services.
.AddCore(options =>
{
// ...
})

// Register the OpenIddict server handler.
.AddServer(options =>
{
// ...

options.AddEventHandler<OpenIddictServerEvents.HandleConfigurationRequest, MyEventHandler>();
})

// Register the OpenIddict validation handler.
.AddValidation();
}

Read more

OpenIddict RC3 is out

OpenIddict RC3 is now available on NuGet.org:

What's new in this release?

The OpenIddict services registration APIs have been revamped

In this release, we focused on reworking the OpenIddict registration APIs to offer a better user experience.

As part of this change, we split the OpenIddict services into three areas - Core, Server and Validation - and the IServiceCollection APIs have been updated to reflect that:

Each specialized builder only exposes the options that are relevant to its specific area:

Of course, the calls to AddCore(), AddServer() and AddValidation() can be chained:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
services.AddOpenIddict()

// Register the OpenIddict core services.
.AddCore(options =>
{
// Register the Entity Framework stores and models.
options.UseEntityFrameworkCore()
.UseDbContext<ApplicationDbContext>();
})

// Register the OpenIddict server handler.
.AddServer(options =>
{
// Register the ASP.NET Core MVC binder used by OpenIddict.
// Note: if you don't call this method, you won't be able to
// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
options.UseMvc();

// Enable the authorization, logout, token and userinfo endpoints.
options.EnableAuthorizationEndpoint("/connect/authorize")
.EnableLogoutEndpoint("/connect/logout")
.EnableTokenEndpoint("/connect/token")
.EnableUserinfoEndpoint("/api/userinfo");

// Note: the Mvc.Client sample only uses the code flow and the password flow, but you
// can enable the other flows if you need to support implicit or client credentials.
options.AllowAuthorizationCodeFlow()
.AllowPasswordFlow()
.AllowRefreshTokenFlow();

// During development, you can disable the HTTPS requirement.
options.DisableHttpsRequirement();
})

// Register the OpenIddict validation handler.
// Note: the OpenIddict validation handler is only compatible with the
// default token format or with reference tokens and cannot be used with
// JWT tokens. For JWT tokens, use the Microsoft JWT bearer handler.
.AddValidation();

Introducing these specialized builders was also a great opportunity to revisit how the OpenIddict entities are registered. In the RC2 bits, this is controlled by the services.AddOpenIddict<...>() method, that determines which entities are used depending on the overload.

In RC3, the generic services.AddOpenIddict<...>() methods have been removed and replaced by a more explicit pattern:

Read more

The aspnet-contrib OAuth 2.0/OpenID 2.0 social providers are now RTM

Today, I'm really pleased to announce that the aspnet-contrib social providers are now RTM.

I'd like to thank all the contributors – 47 at the time of writing – who helped make that possible, with a special mention to Chino Chang, who did a fantastic job by manually porting most of the social providers to the ASP.NET Core 2.0 authentication stack: sir, you rock!

Each provider comes with 2 versions: one for ASP.NET Core 1.x and one for ASP.NET Core 2.x. You can find the complete list at the end of this post.

Both versions will receive security updates/bug fixes but new providers will be backported to ASP.NET Core 1.x only if there's enough demand. If you still run an ASP.NET Core 1.x application, let us know!

OAuth 2.0 social providers

Package nameVersion for ASP.NET Core 1.xVersion for ASP.NET Core 2.x
AspNet.Security.OAuth.Amazon1.0.02.0.0
AspNet.Security.OAuth.ArcGIS1.0.02.0.0
AspNet.Security.OAuth.Asana1.0.02.0.0
AspNet.Security.OAuth.Autodesk1.0.02.0.0
AspNet.Security.OAuth.Automatic1.0.02.0.0
AspNet.Security.OAuth.BattleNet1.0.02.0.0
AspNet.Security.OAuth.Beam1.0.02.0.0
AspNet.Security.OAuth.Bitbucket1.0.02.0.0
AspNet.Security.OAuth.Buffer1.0.02.0.0
AspNet.Security.OAuth.CiscoSpark1.0.02.0.0
AspNet.Security.OAuth.DeviantArt1.0.02.0.0
AspNet.Security.OAuth.Discord1.0.02.0.0
AspNet.Security.OAuth.Dropbox1.0.02.0.0
AspNet.Security.OAuth.EVEOnline1.0.02.0.0
AspNet.Security.OAuth.Fitbit1.0.02.0.0
AspNet.Security.OAuth.Foursquare1.0.02.0.0
AspNet.Security.OAuth.GitHub1.0.02.0.0
AspNet.Security.OAuth.Gitter1.0.02.0.0
AspNet.Security.OAuth.HealthGraph1.0.02.0.0
AspNet.Security.OAuth.Imgur1.0.02.0.0
AspNet.Security.OAuth.Instagram1.0.02.0.0
AspNet.Security.OAuth.LinkedIn1.0.02.0.0
AspNet.Security.OAuth.MailChimp1.0.02.0.0
AspNet.Security.OAuth.Myob1.0.02.0.0
AspNet.Security.OAuth.Onshape1.0.02.0.0
AspNet.Security.OAuth.Patreon1.0.02.0.0
AspNet.Security.OAuth.Paypal1.0.02.0.0
AspNet.Security.OAuth.QQ1.0.02.0.0
AspNet.Security.OAuth.Reddit1.0.02.0.0
AspNet.Security.OAuth.Salesforce1.0.02.0.0
AspNet.Security.OAuth.Slack1.0.02.0.0
AspNet.Security.OAuth.SoundCloud1.0.02.0.0
AspNet.Security.OAuth.Spotify1.0.02.0.0
AspNet.Security.OAuth.StackExchange1.0.02.0.0
AspNet.Security.OAuth.Strava1.0.02.0.0
AspNet.Security.OAuth.Twitch1.0.02.0.0
AspNet.Security.OAuth.Untappd1.0.02.0.0
AspNet.Security.OAuth.Vimeo1.0.02.0.0
AspNet.Security.OAuth.VisualStudio1.0.02.0.0
AspNet.Security.OAuth.Vkontakte1.0.02.0.0
AspNet.Security.OAuth.Weibo1.0.02.0.0
AspNet.Security.OAuth.Weixin1.0.02.0.0
AspNet.Security.OAuth.WordPress1.0.02.0.0
AspNet.Security.OAuth.Yahoo1.0.02.0.0
AspNet.Security.OAuth.Yammer1.0.02.0.0
AspNet.Security.OAuth.Yandex1.0.02.0.0

OpenID 2.0 social providers

Package nameVersion for ASP.NET Core 1.xVersion for ASP.NET Core 2.x
AspNet.Security.OpenId1.0.02.0.0
AspNet.Security.OpenId.Steam1.0.02.0.0

OpenIddict RC2 is now on NuGet.org

Earlier today, new OpenIddict packages were pushed to NuGet.org:

What's new?

Starting with RC2, using OpenIddict with third-party client applications (i.e applications you don't own and are managed by someone else) is officially supported. For that, new features – that were still work in progress in the previous iterations – have been added:

  • A new application permissions feature was added, which allows controlling and limiting the features a client application can use. For instance, to allow a client application to use only the authorization code flow and the logout endpoint, the following permissions can be granted:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
await _applicationManager.CreateAsync(new OpenIddictApplicationDescriptor
{
ClientId = "mvc",
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654",
DisplayName = "MVC client application",
PostLogoutRedirectUris = { new Uri("http://localhost:53507/signout-callback-oidc") },
RedirectUris = { new Uri("http://localhost:53507/signin-oidc") },
Permissions =
{
OpenIddictConstants.Permissions.Endpoints.Authorization,
OpenIddictConstants.Permissions.Endpoints.Logout,
OpenIddictConstants.Permissions.Endpoints.Token,

OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode
}
});

For more information about this feature, you can read the corresponding documentation.

  • A new opt-in scope validation option was added. When it is enabled, OpenIddict automatically rejects authorization and token requests that specify unregistered scopes. Scopes can be registered statically using options.RegisterScopes([list of authorized scopes]) or dynamically, using OpenIddictScopeManager.CreateAsync():
1
2
3
4
5
6
7
8
9
10
11
12
services.AddOpenIddict(options =>
{
// ...

// Mark the "email" and "profile" scopes as valid scopes.
options.RegisterScopes(OpenIdConnectConstants.Scopes.Email,
OpenIdConnectConstants.Scopes.Profile);

// Enable scope validation, so that authorization and token requests
// that specify unregistered scopes are automatically rejected.
options.EnableScopeValidation();
});
1
2
3
4
5
6
await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor
{
Description = "Grants access to the reporting API",
DisplayName = "Reporting API",
Name = "reporting"
});
  • The introspection endpoint was updated to reject access tokens that don't have any audience (since OpenIddict can no longer assume all the registered applications are fully trusted). This change requires updating your code to explicitly attach a resource to your tokens.
1
2
3
4
5
6
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(identity),
new AuthenticationProperties(),
OpenIdConnectServerDefaults.AuthenticationScheme);

ticket.SetResources("reporting");

Read more