Accessing Claims Returned in ASP.NET MVC 5 Identity

One of the nice new features that ASP.NET MVC 5 includes is the ability to authenticate with external identity providers such as Windows Live, Google, Facebook etc using OAuth 2 or OpenID. It makes this task very easy and the default MVC 5 templates provide an example of how this is setup and how to tie this in with ASP.Identity.

If you are upgrading an existing application to MVC 5 and want to make use of the new authentication features but don’t wish to upgrade your existing identity management you are going to need access to the available claims.

The default template utilises the ExternalLoginCallBack action to receive the notification back from the external identity provider. A snippet below shows the applicable default code.

[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
	var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
        if (loginInfo == null)
	{
        	return RedirectToAction("Login");
	}

        // Remainder of method excluded
}

The loginInfo object only makes available the default username, the provider (e.g. Google) and the providers’ key. In addition to those properties we are also interested in obtaining at a minimum claims such as the email address.

To access the individual claims you must get the current claims identity via AuthenticationManager.AuthenticateAsync. Note, the authentication result will be null if the user is not authenticated.

var authResult = await AuthenticationManager.AuthenticateAsync(DefaultAuthenticationTypes.ExternalCookie);
            
if (authResult != null && authResult.Identity != null && authResult.Identity.IsAuthenticated)
{
	var claimsIdentity = authResult.Identity;
	var providerKeyClaim = claimsIdentity.FindFirst(ClaimTypes.NameIdentifier);

	var providerKey = providerKeyClaim.Value;
	var issuer = providerKeyClaim.Issuer;
	var name = claimsIdentity.FindFirstValue(ClaimTypes.Name);
	var emailAddress = claimsIdentity.FindFirstValue(ClaimTypes.Email);

	// Utilise claims
}
else 
{
	// User not authenticated
}

Handling SQL Azure Transient Faults with LightSpeed and EntLib Retry Strategy

On a recent project deployed to Windows Azure we utilized Mindscape’s Lightspeed ORM for accessing the SQL Azure database. Unfortunately LightSpeed doesn’t offer anything in-built to handle transient faults or retries so I have come up with a work around to implementing transient faulting handling with retries on the unit of work which works best for querying using Lightspeeds querying objects but also allows for execution of Linq queries wrapped within a retry policy.

We utilized the Enterprise Library Transient Fault Handling block which can be installed via the Nuget Package Manager Console.

Install-Package EnterpriseLibrary.TransientFaultHandling.Data

LightSpeed’s default behavior is to open a database connection on first attempt to query the database and keep that connection open until the unit of work is disposed of. In the case of our web app, the unit of work is very short lived so connections aren’t open for long periods of time.

In the case of thick or rich client application the default connection strategy isn’t good if the unit of work is kept alive for long periods.

LightSpeed offers extensibility by the way of a ConnectionStrategy that is set on the unit or work to customize the connection per unit of work approach to ensure that when we need to retry on a transient fault that the connection is either created or re-opened depending on its current state.

Create the Azure Connection Strategy.

public class AzureConnectionStrategy : ConnectionStrategy
{
    private IDbConnection _connection;

    public AzureConnectionStrategy(LightSpeedContext context) : base(context) { }

    public IDbConnection CurrentConnection
    {
        get
        {
            return Connection;
        }
    }

    protected override IDbConnection Connection
    {
        get
        {
            if (IsConnectionOpen)
                return _connection;

            if (_connection != null)
            {
                _connection.Open();
            }
            else
            {
                _connection = Context.DataProviderObjectFactory.CreateConnection();
                _connection.ConnectionString = Context.ConnectionString;

                _connection.Open();
            }

            return _connection;
        }
    }

    public void CloseConnection()
    {
        if (_connection != null)
        {
            _connection.Dispose();
            _connection = null;
        }
    }

    public bool IsConnectionOpen
    {
        get { return _connection != null && _connection.State != ConnectionState.Closed; }
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_connection != null)
            {
                _connection.Dispose();
                _connection = null;
            }
        }

        base.Dispose(disposing);
    }
}

Next, we create a new unit of work class inheriting from your unit of work created via the designer and override all built in methods for executing LightSpeed queries and save changes.

public class AzureCoreUnitOfWork : CoreModelUnitOfWork
{
    public override long Count(Mindscape.LightSpeed.Querying.Query query)
    {
        return ExecuteInternal<long>(() => { return base.Count(query); });
    }

    public override int Execute(Mindscape.LightSpeed.Querying.ProcedureQuery query)
    {
        return ExecuteInternal<int>(() => { return base.Execute(query); });
    }

    public override IList<TEntity> Find<TEntity>(Mindscape.LightSpeed.Querying.ProcedureQuery query)
    {
        return ExecuteInternal<IList<TEntity>>(() => { return base.Find<TEntity>(query); });
    }

    public override object Calculate(Mindscape.LightSpeed.Querying.ProcedureQuery query)
    {
        return ExecuteInternal<object>(() => { return base.Calculate(query); });
    }

    public override object Calculate(string calculation, Mindscape.LightSpeed.Querying.IdentifierExpression attribute, Mindscape.LightSpeed.Querying.Query query)
    {
        return ExecuteInternal<object>(() => { return base.Calculate(calculation, attribute, query); });
    }

    public override void Find(Mindscape.LightSpeed.Querying.ProcedureQuery query, System.Collections.IList results)
    {
        ExecuteInternal(() => { base.Find(query, results); });
    }

    public override void Find(Mindscape.LightSpeed.Querying.Query query, System.Collections.IList results)
    {
        ExecuteInternal(() => { base.Find(query, results); });
    }

    public override IList<TEntity> FindBySql<TEntity>(System.Data.IDbCommand command)
    {
        return ExecuteInternal<IList<TEntity>>(() => { return base.FindBySql<TEntity>(command); });
    }

    public override void FindGroup(Mindscape.LightSpeed.Querying.Query query, Mindscape.LightSpeed.EntityTuple results)
    {
        ExecuteInternal(() => { base.FindGroup(query, results); });
    }

    public override Mindscape.LightSpeed.Entity FindOne(Mindscape.LightSpeed.Querying.Query query)
    {
        return ExecuteInternal<Mindscape.LightSpeed.Entity>(() => { return base.FindOne(query); });
    }

    public override void SaveChanges(bool reset)
    {
        ExecuteInternal(() =>
        {
            base.SaveChanges(reset);
        });
    }

    public void ExecuteWithRetry(Action action)
    {
        ExecuteInternal(action);
    }

    public T ExecuteWithRetry<T>(Func<T> func)
    {
        return ExecuteInternal(func);
    }

    private void ExecuteInternal(Action action)
    {
        if (RetryPolicy != null)
        {
            RetryPolicy.ExecuteAction(action);
        }
        else
        {
            action.Invoke();
        }
    }

    private T ExecuteInternal<T>(Func<T> func)
    {
        if (RetryPolicy != null)
        {
            return RetryPolicy.ExecuteAction(func);
        }

        return func.Invoke();
    }

    public RetryPolicy RetryPolicy { get; set; }
}

Create a unit of work factory to handle creation of the unit of work and have the connection strategy and retry policy set as required.

public static class UnitOfWorkFactory
{
    public static AzureCoreUnitOfWork CreateUnitOfWork()
    {
        var lightSpeedContext = new LightSpeedContext<AzureCoreUnitOfWork>("CoreModel");
        var unitOfwork = lightSpeedContext.CreateUnitOfWork();

        unitOfwork.ConnectionStrategy = new AzureConnectionStrategy(lightSpeedContext);
        unitOfwork.RetryPolicy = CreateRetryPolicy();

        return unitOfwork;
    }

    private static RetryPolicy CreateRetryPolicy()
    {
        var retryStrategy = new FixedInterval(3, TimeSpan.FromSeconds(1));
        var retryPolicy = new RetryPolicy<SqlDatabaseTransientErrorDetectionStrategy>(retryStrategy);

        return retryPolicy;
    }
}

Querying using LightSpeed’s query objects.

using (var unitOfWork = UnitOfWorkFactory.CreateUnitOfWork())
{
    var query = new Query(typeof(Comment));

    query.QueryExpression = Entity.Attribute(Comment.UserIdField) == userId;
    query.Order = Order.By(Entity.Attribute(Comment.CreatedOnField)).Descending();

    var comments = unitOfWork.Find<Comment>(query);
}

With no way of intercepting Linq queries on the unit of work the query must be executed within a delegate.

using (var unitOfWork = UnitOfWorkFactory.CreateUnitOfWork())
{
    var comments = unitOfWork.ExecuteWithRetry(() =>
    {
        return (from c in unitOfWork.Comments
                where c.UserId == userId
                orderby c.CreatedOn descending
                select c).ToList();
    });
}

Running Averages in SQL Azure without the extended OVER clause

SQL 2012 introduced an extended OVER clause which allows you to restrict the start and end points within a partition. This makes light work when needing to calculate a running total or average. In my specific case we had to find the running average net sales for the previous 13 weeks per store.

The extended OVER Clause is not supported within SQL Azure so I quickly run a comparison of the SQL 2012 query using the OVER clause against an alternative query using CTE’s and compared the IO statistics for both queries.

Firstly, our sales data is stored weekly per store in the Sales table. In my test I populated ~6000 records into the sales table across 20 stores.

SQL 2012 – OVER Clause

SELECT 
	[dt].[Date], 
	[dt].[StoreId],
	AVG([dt].[NetSales])
	OVER 
	(
		PARTITION BY [dt].[StoreId]
		ORDER BY [dt].[Date], [dt].[StoreId]
		ROWS BETWEEN 13 PRECEDING AND 1 PRECEDING
	)
FROM 
	[Sales] [dt]
ORDER BY 
	[dt].[StoreId], [dt].[Date]

IO Statistics

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Sales'. Scan count 1, logical reads 7, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Azure – CTE

WITH [cte] AS 
( 
	SELECT   
		[dt].[Date],
		[dt].[StoreId],
		[dt].[NetSales],
		DENSE_RANK() OVER 
		( 
			PARTITION BY [dt].[StoreId]
			ORDER BY [dt].[Date], [dt].[StoreId]
		) AS [Rank]
		
	FROM  
		[dbo].[Sales] [dt]
)

SELECT
	[c1].[StoreId], 
	[c1].[Date],
	AVG([c2].[NetSales])
FROM 
	cte [c1]
	JOIN cte [c2] ON [c2].[Rank] >= [c1].[Rank] - 13 and [c2].[Rank] < [c1].[Rank] AND [c1].[StoreId] = [c2].[StoreId]
GROUP BY
	[c1].[Rank], [c1].[StoreId], [c1].[Date]
ORDER BY
	[c1].[StoreId], [c1].[Date]

IO Statistics

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Sales'. Scan count 2, logical reads 14, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

There are other ways to achieve the running average and I have only provided one alternative here. Given the chance I would opt for the OVER clause as it not only performs better but also is a lot less complex to write.

Windows Azure Diagnostics Trace – Failed to extract parameter Name=”Message”

From time to time we were not seeing exceptions being written to the WADLogsTable as expected but the exception was still being written to a text file via the TextWriterTraceListener. We were logging all exceptions details including stack trace and inner exceptions (if available).

The configuration used locally as defined in the web.config.

<system.diagnostics>
	<trace>
		<listeners>
			<add name="TextListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="c:\logs\WADTractTest.log" />
			<add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="AzureDiagnostics">
				<filter type="" />
			</add>
			<remove name="Default" />
		</listeners>
	</trace>
</system.diagnostics>

Firing up the Windows Azure Compute Emulator console and reproducing the exceptions that weren’t being written to table storage provided the following error message from MonAgentHost.

[MonAgentHost] Error:     Failed to extract parameter Name="Message" from event 1a535b25-28dc-43ea-b6df-d6955b381e94:4294967295; <Param Name="Message" Value="
[MonAgentHost] Error:     MA EVENT: 2013-12-01T23:38:20.990Z
[MonAgentHost] Error:     2
[MonAgentHost] Error:     12072
[MonAgentHost] Error:     13628
[MonAgentHost] Error:     EtwListener
[MonAgentHost] Error:     0
[MonAgentHost] Error:     0e45c47e-a364-414b-8abe-02defa8
[MonAgentHost] Error:     etw.cpp
[MonAgentHost] Error:     EtwListener::LogXmlEvent
[MonAgentHost] Error:     6626
[MonAgentHost] Error:     ffffffff80010003
[MonAgentHost] Error:     0

From the error message and reflecting on Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener confirmed that each event is being written to an XmlWriter.

In our case, we are using third party assemblies that have been obfuscated and there were control characters in the stack trace which are invalid within XML.

To mitigate the issue before writing anything to the trace the message has to be sanitized by the removal of control characters.

public static string RemoveControlChars(string s)
{
    if (s != null)
    {
        return Regex.Replace(s, @"(?![\r\n])\p{Cc}", "");
    }

    return null;
}