Tuesday, December 19, 2017

How to Inject and Use AngularJS Services in Angular

Angular developers looking to upgrade their codebase from AngularJS to Angular (v2+) will probably use a hybrid approach via the UpgradeModule and migrate their code over time. During this process you'll probably need to inject and re-use one of your existing AngularJS services in an Angular component or provider. You may also need an existing built in AngularJS service (i.e. $routeParams, $location, etc.). This isn't difficult to do, and the steps are below.

1. Upgrade the AngularJS service/provider to Angular

In your Angular app module, use an Angular factory provider that will request the service from the AngularJS $injector.

providers: [
  { provide: 'myAngularJsService', useFactory: ($injector: any) => $injector.get('myAngularJsService'), deps: ['$injector'] },
  { provide: '$routeParams', useFactory: ($injector: any) => $injector.get('$routeParams'), deps: ['$injector'] }
]

2. Inject the upgraded service in Angular

Using @Inject you will be able to inject your upgraded service for use in an Angular component or provider.

export class MyAngularComponent{

constructor(
  @Inject('myAngularJsService') private myAngularJsService,
  @Inject('$routeParams') private $routeParams
)

  //Implementation...
  myAngularJsService.doSomething();
}

3. Optionally create your own service factory

It's advisable to create a separate factory so it is easier to reference and then upon full migration of code easily delete as well. One note that can be the Achilles heel of this method - it requires the ability to be able to ES6 style import your external module. If your legacy AngularJS code is sill using internal modules and not external modules, this will not be possible as using an old style internal module reference in Angular will not work. So if your entire app is already using external modules you can set up your own factory as shown below.

import { MyAngularJsService} from './my-angularJs.service';

export function myAngularJsServiceFactory(i: any) {
  return i.get('myAngularJsService');
}

export const myAngularJsServiceProvider = {
  provide: MyAngularJsService,
  useFactory: myAngularJsServiceFactory,
  deps: ['$injector']
};

Now it can be registered in the app module via the exported provider created above.

import { myAngularJsServiceProvider } from './upgraded-providers';

//app module or module implementation...
providers: [myAngularJsServiceProvider]

In the Angular component or provider where injected, you can just import the service and use directly in the constructor.

import { MyAngularJsService} from './my-angularJs.service';

export class MyAngularComponent{

constructor(
  private myAngularJsService: MyAngularJsService
)

  //Implementation...
  myAngularJsService.doSomething();
}

The advantage of the above method is now upon injecting, you don't have to use the string handle and will get proper type checking.

Friday, November 3, 2017

Accepting the Android SDK License via Android Studio

If you want to debug using an Android emulator as I did for a Cordova project using Visual Studio code, you'll need to go through a series of steps to install the Android SDK (documented here). Once you revisit Visual Studio code and attempt to debug using "Run Android on Emulator," you might run into the following error:
You have not accepted the license agreements of the following SDK components: [Android SDK Platform 26].
Before building your project, you need to accept the license agreements and complete the installation of the missing components using the Android Studio SDK Manager.

There are a slew of ways to solve this problem via the command line documented on this SO post here. However, I also found it easy to open up Android Studio and create a new basic project to trigger the license agreements. Once I created a project, the following licensing dialog was presented that I needed to agree to:


It also prompted me to download additional needed dependencies:


After I did the steps above, I was able to go back to Visual Studio Code and run my Cordova application using the Android emulator.

Thursday, September 21, 2017

How to Import RxJS in Angular Using ES6 Style Imports

Upon building any type of Angular application, you'll eventually need to make a HTTP call and will do this inside of a Provider. The following code illustrates a barebones use of the Http service in Angular for making a call and mapping the response to an Observable.

return this.http.get(apiUrl)
    .map((response:Response) => response.json())
    .catch((error:any) => Observable.throw(error.json().error || 'An error occurred'));

In order to use this code which is based on the ES.Next Observable, you'll need a library like RxJS to use it in your application. Since many IDEs are poor at letting you know exactly which artifacts need to be imported, you'll probably do a Google search and end up with the following:

import 'rxjs/Rx';

The problem with the above is that imports all of RxJS and that is a huge library. The bottom line - don't do that. The proper way is to import the exported Observable, and any operators needed like below:

import { Observable } from "rxjs/Observable";
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/toPromise';  //Only needed if wrapping in a Promise to return

However, notice the inconsistency in the import statements. Can't we just import using the following ES6 syntax for everything?

import { member } from "module-name";

In order to explain this, we must understand that there are different ways to import modules using ES6 based on how modules are bundled and exported. To begin let's look at the ES6 documentation. The above method is to import exported members. Well what about my map, catch, etc. RxJS methods? They are not exported from the 'add' directory as they are meant to be patched from that location, so you must import the physical module where they reside using the following syntax:

import 'module-name';

The RxJS documentation eludes to this process, and what we are doing is patching the Observable's prototype under the covers. If you want to see this in action, manually navigate to the map.js file located in the 'rxjs/add/operator/map' directory within 'node_modules' (or via GitHub), and take a look at the code.

Observable.prototype.map = map;

However, if you wish you can still use the following ES6 import as well:

import { map } from "rxjs/operator/map";

I like this for consistency, but there is a difference in the (2) different styles of importing. The module import and patching of Observable is actually better for size-sensitive bundling needs. You just need to make sure you import all needed methods before they are used. Importing and calling the methods directly safeguards against calling methods that haven't been patched, but at the cost of additional code resulting in a larger bundle size.

So fear not, you aren't sloppily mixing implementation styles with your RxJS import statements. Just understand the different styles and stick to one for consistency. There are some additional best practices about importing these modules once per application and enforcing it during linting if you go the route of patching. You can find out more about that process using rxjs-tslint-rules


Tuesday, February 21, 2017

Using the Azure Mobile Apps Signing Key with JWT Bearer Authentication in ASP.NET Core

Azure Mobile Apps allow users to quickly get up and running using authentication via 3rd party providers. Once registered with your Azure Mobile Apps instance, you can use the appropriate SDK (i.e. JavaScript SDK) to authenticate users, and in turn get a signed JWT back representing the authenticated user and their claims. This JWT can be turned around and sent with requests to the server to authenticate users when making WebAPI calls.

This was also possible in the predecessor to Azure Mobile Apps, named Azure Mobile Services. The 'master' and 'application' keys were readily available from the Azure dashboard. The server receiving the JWT could use the 'master' key and check if the JWT being sent was issued using the identical key. If it was then that is an indicator that the call is properly authenticated.

With Azure Mobile Apps the concept of the 'master' and 'application' keys have been removed from the portal, as well the JavaScript SDK is no longer required to send the 'application' key in the calls to the server. However the concept of the 'signing' key that created the JWT representing the authenticated user still exists. The following steps will help you get started using Azure Mobile Apps and the JavaScript SDK, to call a WebAPI backend using ASP.NET Core.

1. Get the Signing Key for your Azure Mobile Apps Instance


If you want to use JWT Bearer Authentication on the server, you'll need to configure its settings to contain the Signing Key used to generate the JWT passed back to the authenticated client from Azure. This way you can validate calls on the server and that the user was truly authenticated via your Azure Mobile App instance. The nice thing about JSON Web Tokens is they are structured to contain known information (claims) including being able to verify the signing value used and if it matches. 

The signing value is unfortunantly buried on a page outside of the Azure portal. This wild goose chase I went on trying to find the value was a time dump, so hopefully with this information you'll be able to find this quickly. To get the signing key, go to the following URL (best if already signed into Azure):

https://{yoursite}.scm.azurewebsites.net/env
Replace the {yoursite} portion with the name of your Azure instance. A page will load with a ton of configuration about your site. Do a 'find' for the following value: 
WEBSITE_AUTH_SIGNING_KEY
The signing value will be associated with that key that is used for your Azure Mobile Apps instance. Grab it and save for use in the next step.



2. Configure JwtBearerAuthentication in ASP.NET Core

  1. Pull in the NuGet package via project.json in your WebAPI ASP.NET Core project. This will allow us to have the needed bits to configure the ASP.NET Core middleware for JWT Bearer Authentication:
  2. "Microsoft.AspNetCore.Authentication.JwtBearer": "1.1.0-preview1-final"
    
  3. To prevent making Startup.cs too polluted, create a separate partial class (if desired) named something like Startup.auth.cs where we can place the JWT middleware configuration. Then in the Configure method of the main Startup.cs file, add the following to call our new method we'll create below:
  4. ConfigureAuth(app);
    
  5. Add the JWT configuration, and replace the value in the instantiation of SymmetricSecurityKey with the value from your Azure instance we extracted above in Step #1. All of this configuration and setting of the various properties is up to you. At a minimum though I'd recommend making sure that the key was issued from the correct domain, not expired, and signed with the proper key. The key will need to be extracted from its hex value, so the helper is included.
  6. public partial class Startup
    {
    /// <summary>
    /// Sets up JWT middleware configuration for use when authorizing endpoints within this API
    /// </summary>
    /// <param name="app"></param>
    private void ConfigureAuth(IApplicationBuilder app)
    {
     //Todo: place in configuration
     var signingKey = new SymmetricSecurityKey(FromHex("YOUR_WEBSITE_AUTH_SIGNING_KEY_VALUE_HERE"));            
    
     var tokenValidationParameters = new TokenValidationParameters
     {
      RequireSignedTokens = true,
      RequireExpirationTime = true,
      SaveSigninToken = false,
      ValidateActor = false,
    
      // The signing key must match!
      ValidateIssuerSigningKey = true,
      IssuerSigningKey = signingKey,
        
      // Validate the JWT Issuer (iss) claim
      ValidateIssuer = true,
      ValidIssuer = "https://your-site.azurewebsites.net/",
    
      // Validate the JWT Audience (aud) claim
      ValidateAudience = true,
      ValidAudience = "https://your-site.azurewebsites.net/",
    
      // Validate the token expiry
      ValidateLifetime = false,
    
      // If you want to allow a certain amount of clock drift, set that here:
      ClockSkew = TimeSpan.Zero
     };
    
     app.UseJwtBearerAuthentication(new JwtBearerOptions
     {
      AutomaticAuthenticate = true,
      AutomaticChallenge = true,
      TokenValidationParameters = tokenValidationParameters
     });
    
    }
    
    /// <summary>
    /// Decodes a Hex string
    /// </summary>
    /// <param name="hex"></param>
    /// <returns>byte[]</returns>
    private static byte[] FromHex(string hex)
    {
     hex = hex.Replace("-", "");
     byte[] raw = new byte[hex.Length / 2];
     for (int i = 0; i < raw.Length; i++)
     {
      raw[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
     }
     return raw;
    }
    }
    
  7. Add the usual Authorize attribute to the API controllers where authentication needs to be enabled.
  8. [Authorize]
    
That's the basics of the setup. You'll want to configure the client to store the JWT retrieved after authenticating for the session of the user, and should be sent as a 'Bearer' Authorization header on the request. This will ensure that all of your calls from the JavaScript client to the WebAPI server instance will be authenticated properly. If the JWT passed in the request header for the call doesn't pass all the validations set in the ConfigureAuth method, the call will fail as desired with a 401 Unauthorized to the client.

Thursday, January 19, 2017

I'm speaking at Modern Apps LIVE! Vegas + Free Webcast on 2/7

Please join me Tuesday, February 7 at 11am PT/2pm ET to preview Modern Apps LIVE! Vegas (a part of Visual Studio LIVE!), and my modern app web sessions via a free webcast. You can sign up at the following link: 

Viva Modern Web Development!

This will preview Modern Apps LIVE! (in partnership with Magenic), and the following session I'll be presenting:
You can also save $500 of the 5-day all-access Best Value Conference Package in Las Vegas (March 13-17) by using my speaker savings code of LVSPK09 when registering for the event. Just use the following link or click the image below.