July 2015 Blog Posts
Enabling Decorators Support in TypeScript 1.5, Visual Studio 2015

Finally installed VS2015, I got the following error in my TypeScript project:

Error    TS1219    Experimental support for decorators is a feature that is subject to change in a future release. Specify '--experimentalDecorators' to remove this warning.

Ok. So, how exactly do you do this if you're not the one invoking the TSC compile action? Visual Studio TypeScript plugin is invoking the compiler option. Hmm. tsconfig.json compiler config doens't seem to be picked up by the TypeScript plug-in.

To enable decorator support, unload your TypeScript project. Right click, Edit Project .csproj. Find the TypeScript compiler option section and add the following (Line 14 and 15):

Code Snippet
  1. <PropertyGroup Condition="'$(Configuration)' == 'Debug'">
  2.   <TypeScriptModuleKind>amd</TypeScriptModuleKind>
  3.   <TypeScriptCompileOnSaveEnabled>True</TypeScriptCompileOnSaveEnabled>
  4.   <TypeScriptTarget>ES5</TypeScriptTarget>
  5.   <TypeScriptNoImplicitAny>False</TypeScriptNoImplicitAny>
  6.   <TypeScriptRemoveComments>False</TypeScriptRemoveComments>
  7.   <TypeScriptOutFile />
  8.   <TypeScriptOutDir />
  9.   <TypeScriptGeneratesDeclarations>False</TypeScriptGeneratesDeclarations>
  10.   <TypeScriptNoEmitOnError>True</TypeScriptNoEmitOnError>
  11.   <TypeScriptSourceMap>True</TypeScriptSourceMap>
  12.   <TypeScriptMapRoot />
  13.   <TypeScriptSourceRoot />
  14.   <TypeScriptEmitDecoratorMetadata>True</TypeScriptEmitDecoratorMetadata>
  15.   <TypeScriptExperimentalDecorators>True</TypeScriptExperimentalDecorators>
  16. </PropertyGroup>

Also, seems like the TypeScript 1.5 RTM plugin will respect the decorator compile options when you use SAVE-ON-COMPILE. :)

Hope that helps,
Brian Chavez

2 Comments Filed Under [ ASP.NET ]
ASP.NET Identity, OAuth 2 Social Login, Web API 2, and MVC 5 SPAs

image Wow, that's a mouthful. I feel like that lady on the right sometimes.

So, I spent a few days studying SPA applications and how to use external social logins like Facebook and Google with Web API 2.

It's not for the faint of heart, especially when Microsoft's Web API template with VS 2013 is broken. Microsoft's template implementation is about 70% right.

Pile on top, all the inconsistent Web API login routes to think about. See this post for what's broken. I mostly agree to some extent. In addition, it seems like most "Web API" tutorials out there are outdated. Most of the tutorials have a user's browser parsing an obscure "auth_token" somewhere along the OAuth login flow. Surprise! In MVC5 + WebAPI2 + Social Login, there is no auth token homie! Now, it's all handled by the middleware. LoL. Eh, well, whatever, I didn't want to expose a scary sensitive auth token to the user's browser anyway. But I digress.

Before we begin, some articles & references that will help you

Distilled down to a single blog post, I present to you, ASP.NET Identity's OAuth2 Social Login flow with Web API 2:

So it begins, a new user arrives

Figure A:

image

Figure A, the user arrives and hits your MVC app. Your MVC app sends down your SPA. The user navigates to a login page and Figure B happens.

Figure B:

image

Your SPA app consumes the JSON from ExternalLogins call and renders some choices. In Figure B, Google and Facebook are configured on the server's OWIN middleware. Cool.

Figure C:

image

The user decides to login with "Facebook". In Figure C, it's a full-page navigation to the URL of /ExternalLogin?provider=Facebook. Not an AJAX call.

AuthController.cs (originally AccountController.cs from the original template) sees this request, and processing goes up to a ChallengeResult:

// GET api/auth/ExternalLogin
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
[AllowAnonymous]
[Route("ExternalLogin", Name = "ExternalLogin")]
public async Task<IHttpActionResult> GetExternalLogin(string provider, string error = null)
{
    if( error != null )
    {
        return Redirect(Url.Content("~/") + "#error=" + Uri.EscapeDataString(error));
    }

    //ANONYMOUS will always get trapped here.
    if( !User.Identity.IsAuthenticated )
    {
        return new ChallengeResult(provider, this);
    }

A ChallengeResult is returned to the anonymous user. A ChallengeResult looks like:

public class ChallengeResult : IHttpActionResult
{
    public ChallengeResult(string loginProvider, ApiController controller)
    {
        LoginProvider = loginProvider;
        Request = controller.Request;
    }
    public string LoginProvider { get; set; }
    public HttpRequestMessage Request { get; set; }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        Request.GetOwinContext().Authentication.Challenge(LoginProvider);
        var response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
        {
            RequestMessage = Request
        };
        return Task.FromResult(response);
    }
}

ChallengeResult has an HTTP Status Code of Unauthorized. When MVC is executing the ChallengeResult, MVC notifies the OWIN middleware of an Authentication.Challenge("Facebook"). The unauthorized status code triggers the OWIN middleware to wake up and process the pending Authorization.Challenge, and transform the unauthorized status into a 302 redirect to an off-site login provider (Facebook in this case) for authorization as shown in Step 7 in Figure C above. Take note, in particular, the 302 redirect to Facebook has "redirect_uri=http://webapp.com/signin-facebook". This is a callback into the middleware that the user's browser will hit after they are finished authenticating and authorizing your web app on Facebook. In addition, take 2nd note, the middleware has instructed the browser to set a Correlation.Facebook cookie in Step 7 in Figure C above.

The user's browser follows the redirect as shown in Figure D below:

Figure D:

image 

In Figure D Step 8 above, the user authenticates with Facebook and authorizes WebApp.

Then, Figure D Step 9, Facebook issues a 302 redirect to the user's browser back on the OWIN middleware endpoint /signin-facebook. The browser follows the redirect shown in Figure E Step 10 below:

Figure E:

image

Side Note: When the middleware receives the /signin-facebook callback, the middleware needs to check if the current callback originated from the WebApp to prevent a CSRF attack. The Facebook middleware inspects Correlation.Facebook cookie and matches the cookie with some encrypted state inside the callback that was part of the query string on the way out to Facebook and on the way back in from Facebook. Hewf. Stay with me now...

Next, the OWIN middleware uses the code query string to setup a backchannel with Facebook. The code=AQABC... from Figure E, Step 10, is a code to get a real access_token. The access token is then used to query Facebook's Open Graph to get some claims about the user, like their Name, picture, email, etc ... Step 11 and Step 12 in Figure E.

Finally, the OWIN middleware has enough information to create a ClaimsIdentity of an external login, and does so by granting an AuthenticationTicket associated with an ExternalCookie authentication type. The Facebook middleware packs all the claims from the Facebook backchannel into this cookie called .AspNet.ExternalCookie as shown in Step 13 in Figure E.

Lastly, the OWIN middleware redirects the user's browser *back* into the MVC layer (remember our old URL that originally kicked off the process? /api/auth/ExternalLogin). Yep. I know. But, this time, we're going to get past the original ChallengeResult because were coming back as an externally authenticated user with the help of [OverrideAuthentication]+ [HostAuthentication(ExternalCookie)], which sets up the User.Identity.IsAuthenticated to return true skipping the challenge as shown below:

// GET api/auth/ExternalLogin
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
[AllowAnonymous]
[Route("ExternalLogin", Name = "ExternalLogin")]
public async Task<IHttpActionResult> GetExternalLogin(string provider, string error = null)
{
    if( error != null )
    {
        return Redirect(Url.Content("~/") + "#error=" + Uri.EscapeDataString(error));
    }

    //ANONYMOUS will always get trapped here.
    if( !User.Identity.IsAuthenticated )
    {
        return new ChallengeResult(provider, this);
    }

    var externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);

    if( externalLogin == null )
    {
        return InternalServerError();
    }

    if( externalLogin.LoginProvider != provider )
    {
        Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
        return new ChallengeResult(provider, this);
    }

    var user = await this.UserManager.FindAsync(new UserLoginInfo(externalLogin.LoginProvider, externalLogin.ProviderKey));

    var hasRegistered = user != null;

    if( hasRegistered )
    {
        Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);

        var oAuthIdentity = await this.UserManager.GenerateUserIdentityAsync(user, OAuthDefaults.AuthenticationType);
        var cookieIdentity = await this.UserManager.GenerateUserIdentityAsync(user, CookieAuthenticationDefaults.AuthenticationType);

        var properties = this.UserManager.CreateProperties(user);

        Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
        return Redirect(Url.Content("~/app#home"));
    }

    //so even if we get here, ANONYMOUS will never get this far.
    //The browser has been authorized by an external provider.
    //The next step is for the browser to call /RegisterExternal
    //to register an account. We'll throw up an authorized splash to confirm.

    return Redirect(Url.Content("~/app#/authorized"));
}

This time, we hit the bottom of the /api/ExternalLogin call.  This is the part where you can customize the login process. The OWIN middleware has already done the hard work for you. In the case where the user has never registered on WebApp, you have a small window of time to use the ExternalCookie login information for purposes of registration. We redirect the user's browser to ~/app#/authorized, where we throw up a screen and ask for a username. The user clicks "Register" which then calls our Web API endpoint /api/auth/RegisterExternal.

// POST api/auth/RegisterExternal
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
[Route("RegisterExternal")]
public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model)
{
    if( !ModelState.IsValid )
    {
        return BadRequest(ModelState);
    }

    var info = await Authentication.GetExternalLoginInfoAsync();
    if( info == null )
    {
        return InternalServerError();
    }

    var user = await this.UserManager.FindAsync(info.Login);
    
    var hasRegistered = user != null;
    
    if( hasRegistered )
    {
        return BadRequest("External user already registered.");
    }

    user = new User() { UserName = model.Email, Email = model.Email };

    var result = await this.UserManager.CreateAsync(user);
    if( !result.Succeeded )
    {
        return GetErrorResult(result);
    }

    result = await this.UserManager.AddLoginAsync(user.Id, info.Login);
    if( !result.Succeeded )
    {
        return GetErrorResult(result);
    }
    return Ok();
}

And our user now has a local account.

Next blog post I'll will show how to transform the ExternalCookie AuthenticationType into a local usable access_token for Token Barer Authentication for Web API during the life time of the SPA application.

HTH,
Brian Chavez

9 Comments Filed Under [ ASP.NET C# ]
Newbies Guide to Aurelia and TypeScript

npxbj[1] So, after about 24 hours trying to understand and establish a workflow for a new JS framework called Aurelia and TypeScript, I think I've finally forged a path to use both in harmony in Visual Studio 2013.

First, some important concepts

Aurelia is written in ES6/ES7. Aurelia is distributed through jspm. The Aurelia distribution through jspm is ES5 code. Aurelia ES5 via jspm is runnable inside your browser.

If you plan on writing view models in TypeScrypt then all you need to be concerned about is transpiling your App.ts (*.ts) View Models into ES5 code.

Get the Tools

To get setup, you're going to need:

NodeJS

Git

Be sure you can access git and npm from the command line before continuing.

Setup the Framework

Next, in a command prompt make sure you have the following installed:

npm install jspm -g

npm install tsd -g

At this point, you should go to your github account and create an access token for jspm because jspm will hit a lot of github repos when resolving dependencies. This means you can run up against an anonymous rate limit rather quickly. I did! Also, ensure the token you generate also has "public_repo" permission with it; otherwise you'll run up against the same rate limit as an anonymous user and jspm requests will get 404s & projects not found. Do not skip this step.

Use the following command to configure jspm and github:

jspm registry config github

Create a working folder for your project, and:

tsd init

jspm install aurelia-framework

jspm install aurelia-bootstrapper

Copy all the *.d.ts type definitions inside "jspm_packages\github\aurelia" into a new folder "\typings\aurelia".

Then run in the root of your project folder:

tsd rebundle

>> added:
    - aurelia/aurelia-binding.d.ts
    - aurelia/aurelia-dependency-injection.d.ts
    - aurelia/aurelia-event-aggregator.d.ts
    - aurelia/aurelia-framework.d.ts
    - aurelia/aurelia-history-browser.d.ts
    - aurelia/aurelia-history.d.ts
    - aurelia/aurelia-loader-default.d.ts
    - aurelia/aurelia-loader.d.ts
    - aurelia/aurelia-logging-console.d.ts
    - aurelia/aurelia-logging.d.ts
    - aurelia/aurelia-metadata.d.ts
    - aurelia/aurelia-path.d.ts
    - aurelia/aurelia-route-recognizer.d.ts
    - aurelia/aurelia-router.d.ts
    - aurelia/aurelia-task-queue.d.ts
    - aurelia/aurelia-templating-binding.d.ts
    - aurelia/aurelia-templating.d.ts

Your "typings\tsd.d.ts" file should have all the necessary references now. (TIP: Setup a gulp script to automate this process, You'll need to re-do this every time you update Aurelia).

Almost there ...

Get corejs.d.ts and es6.d.ts from this repo (or if using gulp/npm typescript: node_modules\typescript\bin\lib.es6.d.ts) and add both into "typings\". You will need to reference these in your *.ts files.

At this point, you can create an empty MVC project inside your working folder at root level. Configure your routes and add a simple view with (no layout). Your .csproj file should be in the same directory with your proejct.json and config.js file.

If you're using Visual Studio, make sure your TypeScript compiler "Module system" is set to AMD. Project > Properties > TypeScript Build: Module system

image 

Create an app.ts file in the root project folder:

/// <reference path="typings/tsd.d.ts" />
/// <reference path="typings/es6.d.ts" />
/// <reference path="typings/core-js.d.ts" />

import {Router} from 'aurelia-router';

export class App {
    constructor() {
        this.message = "Hello World";
    }

    message:string;
}

console.log("THIS APP RAN!");

SAVE. Your app.ts -> app.js should have AMD module layout.

Create an app.html view in the root project folder:

<template>
    
    <h2>My App</h2>
    
    <span>${message}</span>

</template>

Save.

Create an index.cshtml file (Razor / No Shared Layout):

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <script src="~/jspm_packages/system.js"></script>
    <script src="~/config.js"></script>
    <title></title>
</head>
<body aurelia-app>
    
    <script>
        System.import("aurelia-bootstrapper");
    </script>
</body>
</html>

SAVE.

Run the app, make a request for the application index and poof!

image

Much success. :)

 

PRO TIP: Going the extra mile to get decorators working with Dependency Injection

 

SEE UPDATE: Using TypeScript 1.5 RTM and VS2015

If you're using Gulp, this is really easy, use a tsconfig.json  and enable emitDecoratorMetadata CompilerOptions flag. If you're using gulp-typescript pass a configure options object with emitDecoratorMetadata : true.

If you're using Visual Studio 2013, unload your .csproj, and edit the configuration. Locate the following MSBuild props and add TypeScriptAdditionalFlags:

<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
    <TypeScriptModuleKind>amd</TypeScriptModuleKind>
    <TypeScriptAdditionalFlags> $(TypeScriptAdditionalFlags) --emitDecoratorMetadata </TypeScriptAdditionalFlags>
</PropertyGroup>

SAVE. Reload the project. Restart Visual Studio to make sure these changes really take effect. Hit the "Rebuild" on your project and you should now see your decorators working. How? You should see __metadata in the output:

image

Unfortunately, modifying your .csproj will only work with "MSBuild" invocations like "Build/Rebuild". This will not work with compile-on-SAVE option inside VS. If you want a workflow of compile-on-SAVE, you'll ultimately need to use gulp. I really tried hunting for the compiler options VS uses for compiling-on-SAVE but I couldn't find the hook point. I suspect it's somewhere deep inside Microsoft's closed-source TypeScript plug-in for VS2013. TypeScriptAdditionalFlags is simply not picked up by the VS 2013 TypeScript plug-in. Hopefully, this changes in TypeScript 1.5 RTM.

If you decide to use gulp for compile-on-SAVE ensure you DISABLE compile-on-save inside Visual Studio your project options:

 image

Otherwise, both gulp and VS will overwrite each other.

PRO TIP: Using jspm to use packages that are not "officially" in the global registry

Credits to @grofit for the tip:

jspm's global register is rather limited compared to something like bower.io. For example, toastr isn't inside the jspm registry, but you could do this:

jspm install github:CodeSeven/toastr

     Looking up github:CodeSeven/toastr
     Updating registry cache...
     Downloading github:[email protected]
ok   Installed CodeSeven/toastr as github:CodeSeven/toastr@^2.1.1 (2.1.1)
ok   Install tree has no forks.

ok   Install complete.

 

PRO TIP: Understanding Aurelia Value Converters

See this: http://jdanyow.github.io/aurelia-converters-sample/. Really good tutorial on value converters.

PRO TIP: Using jQuery Plugins with Aurelia

image

PRO TIP: Don't use SystemJS to load CSS

Use SystemJS for importing JavaScript that is compatible with jspm and bundled for jspm... using SystemJS's CSS plug-in to load CSS will get you in a world of mess. Basically, don't use import "bootstrap.css!" in your ES6 modules. The reason for this is the ES6/SystemJS loader standard was recently changed so CSS ordering is not guaranteed. If you need to load CSS resources in some specified order, use Aurelia's <require from="..."> to load your CSS resources.

If you installed JavaScript libraries manually using "jspm github:user/somerepo" (that isn't part of jspm's universe nor jspm aware) there's a good chance that jspm wont setup dependencies (ie: jQuery) required by "user/somerepo".  Your third-party library can be loaded out of order relative to its dependency (for the exact reason above) because JSPM doesn't know the package's dependencies by default if they are not specified. See this blog post for more info: How to deal with dependencies with non-registry JSPM packages

 

PRO TIP: Careful of camelCase names in View Models

image

Has to do with hyphenations and DOM elements:

http://aurelia.io/docs.html#custom-attributes

* Since it's a dom attribute it's not valid html so Aurelia will automatically hyphenate and try match an attribute.

* Simply put when you have a name that camel case you need to separate it with -

 

Have fun :)

Brian Chavez

2 Comments Filed Under [ Tips & Tricks ASP.NET ]