0.0.10 • Published 6 years ago

azure-auth-client v0.0.10

Weekly downloads
4
License
MIT
Repository
github
Last release
6 years ago

azure-auth-client

npm version

A lightweight abstraction around adal.js. Use this to get identity and access tokens and information about the user using your application.

Example

import AuthClient from "azure-auth-client";

const config = {clientType: "ADAL",
                clientId: "ae33c32e-d2f2-4992-a4b2-51d03e7c8677",
                tenantId: "c834c34e-bbd3-4ea1-c2c2-51daeff91aa32",
                domain: "bar.com"};

const authClient = AuthClient.build(config);

const {ok, token, name, roles} = authClient.getIdentityToken();

const {ok, token, name, roles} = authClient.getAccessToken("foo");

Installing

Using npm:

$ npm install azure-auth-client

Using yarn:

$ yarn add azure-auth-client

Why?

It abstracts you away from adal.js, which you might not want to deal with directly because it's kind of gross. Note that right now, this library intentionally doesn't expose all of the functionality of adal.js - just the functionality for the stuff I care about at the moment. This library also decodes the tokens for you and pulls some potentially relevant identity information out of the token's claims.

Also, if you have a single-page application that interacts with Azure AD in production, you might not want it to do that during development or testing. This abstraction lets you use a dummy implementation that uses a pre-configured identity, identity token, and access tokens, so you don't have to integrate when you don't want to. You may or may not find this helpful.

Fine, how?

Making a config

You'll need to create an Azure auth config object.

Here's what a "real" Azure auth config object might look like:

{clientType: "ADAL",
 clientId: "ae33c32e-d2f2-4992-a4b2-51d03e7c8677",
 tenantId: "c834c34e-bbd3-4ea1-c2c2-51daeff91aa32",
 domain: "bar.com"}

Setting the clientType to 'ADAL' means you'll use the real implementation that talks to Azure AD.

  • clientId - The client ID of your application (either a single-page application or a traditional web application, depending on your situation) from Azure
  • tenantId - The ID of your Azure tenant
  • domain - The AD domain

You can also pass tenantName rather than tenantId if you feel like it, as well as override the default values of adal.js configuration properties that I chose.

If you don't feel like talking to Azure AD and want to pretend to be an arbitrary user for testing, use the dummy implementation by setting clientType to 'DUMMY'.

When using the dummy implementation, the identityToken, identity, accessTokens, and accessTokenResponses configuration properties can be used to configure the tokens returned by the dummy client.

Here's an example of what a dummy Azure auth config might look like:

{clientType: "DUMMY",
 identityToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpPTkVTLCBGUkVEIiwiZmFtaWx5X25hbWUiOiJKT05FUyIsImdpdmVuX25hbWUiOiJGUkVEIiwicm9sZXMiOlsiQURNSU4iXSwiaWF0IjoxNTE2MjM5MDIyfQ.IfakCBAhIYgkUU12-bRK4r6Boo2FVuW1kd9POylK"}

See the Dummy configuration section(Dummy Configuration) section for a full overview of the different ways to configure the dummy client.

It's possible you already have some kind of configuration object to hold your app's configuration - something that looks like this:

{foo: "bar",
 baz: 42,
 apiConfig: {url: "localhost:3000/api"}}

If you do, just stick your Azure auth config object into your app config as azureAuthConfig, like this:

{foo: "bar",
 baz: 42,
 apiConfig: {url: "localhost:3000/api"},
 azureAuthConfig: {clientType: "ADAL",
                   clientId: "ae33c32e-d2f2-4992-a4b2-51d03e7c8677",
                   tenantId: "c834c34e-bbd3-4ea1-c2c2-51daeff91aa32",
                   domain: "bar.com"}}

If not, you can just use the standalone Azure auth config to create a client instance.

Creating a client

Once you have your Azure auth config object - either as part of your app config object or by itself - add the following import where ever you do your configuring:

import AuthClient from 'azure-auth-client'

Then, use your config to create an AuthClient:

const authClient = AuthClient.build(config);

You can pass this guy around wherever you want. In a React or Redux app, injecting it via props or state or middleware seems like the best way to use it. Eventually, there should be examples of doing this somewhere.

Getting tokens

Now you can use this auth client object anywhere to get an identity token or access tokens for different resources, like this:

const response = authClient.getIdentityToken();
  => {ok: true, token: "eyJ0eXAiOi...", decodedToken: ...}

const response = authClient.getAccessToken("foo");
  => {ok: true, token: "eyJ0eXAiOi...", decodedToken: ...}

Token Responses

Success

If we got a token, the ok property of the response will be true, and the token and decodedToken properties will be present:

{ok: true,
 loginTriggered: false,
 accessDenied: false,
 token: "eyJ0eXAiOi...",
 decodedToken: ...}

Login Triggered

If the user needs to be logged in, both token methods will trigger a redirect to the login page and the loginTriggered property will be true:

{ok: true,
 loginTriggered: true,
 accessDenied: false,
 reason: "login-triggered",
 resource: "5ef91fa4-6170-4c8e-b946-1d99e7d8d59c"}

Access Denied

If the user is not authorized to get a token for the resource, the reason property will be set to access-denied:

{ok: true,
 loginTriggered: true,
 accessDenied: true,
 reason: "access-denied",
 resource: "5ef91fa4-6170-4c8e-b946-1d99e7d8d59c",
 message: "..."}

Invalid resource

If you ask for a token for an invalid resource, the reason property will be set to invalid-resource:

{ok: true,
 loginTriggered: true,
 accessDenied: false,
 reason: "invalid-resource",
 resource: "5ef91fa4-6170-4c8e-b946-1d99e7d8d59c"}

Generic failure

If we fail to get a token for some other reason, the ok property of the response will be false and the reason property will contain a code indicating why we were unable to get a token:

{ok: true,
 loginTriggered: false,
 accessDenied: false,
 reason: "some-reason",
 resource: "5ef91fa4-6170-4c8e-b946-1d99e7d8d59c"}

Using tokens

Assuming you successfully retrieved a token, the response from these functions contains both the raw token and all the data contained inside of it, which means you don't have to decode it yourself or do any other manual parsing.

Here's an example of what you might get. Results will vary depending on how your application is registered in Azure Active Directory.

{
  "ok": true,
  "token": "eyJ0eXAiOi...",
  "decodedToken": {
    "aud": "abc4e33e-e805-0992-a4b2-59f1ef7e8e7a",
    "iss": "https://foo.com/bar",
    "iat": 1518675421,
    "nbf": 1518675421,
    "exp": 1518679421,
    "aio": "XyZaew323grgerEWfewfWEewf4trs6455645eWE222f#fwefewfe",
    "amr": [
      "wia"
    ],
    "family_name": "JONES",
    "given_name": "FRED",
    "in_corp": "true",
    "ipaddr": "123.45.678.0",
    "name": "JONES, FRED",
    "nonce": "42af75a6-c56c-4bd4-bbg9-ba42d67ad7ea",
    "oid": "beef43456e6dc-54se-423e-bea3112eda57",
    "onprem_sid": "S-1-5-21-1231242546-6544342323-3252352364-23235324",
    "sub": "VewaeEFW-wewEW2-3220230aeawefw032a2a3332a32",
    "roles": ["Ninja", "Lawyer"],
    "tid": "ab23cdef-ad22-ab43-a32d-e56r12233aea",
    "unique_name": "fredjones@baz.com",
    "upn": "fredjones@baz.com",
    "uti": "awer2323aeweaQW323eawf",
    "ver": "1.0"
  },
  "name": "JONES, FRED",
  "familyName": "FRED",
  "givenName": "JONES"
  "roles": ["Ninja", "Lawyer"]
}

When you have an identity token for your own application's backend API or an access token for some other API, you can pass it in an Authorization header with an authentication scheme of Bearer to call the appropriate secured service:

const tokenResponse = authClient.getAccessToken("abc4e33e-e805-0992-a4b2-59f1ef7e8e7a");

if(!tokenResponse.ok) {
  throw new Error("Failed to get access token.");
}

const headers = new Headers();

headers.append("Authorization", `Bearer ${tokenResponse.token}`);

const options = {method: 'GET', headers, mode: "cors"};

const barResponse = await fetch("https://foo.com/bar", options);

Dummy Configuration

The dummy client can be configured to return hardcoded identity and access tokens. Additionally, access token responses can be used to trigger various token-related error conditions.

Identity Token

To use a hardcoded identity token, set identityToken to a valid token with the name, family_name, given_name, and roles claims:

{clientType: "DUMMY",
 identityToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpPTkVTLCBGUkVEIiwiZmFtaWx5X25hbWUiOiJKT05FUyIsImdpdmVuX25hbWUiOiJGUkVEIiwicm9sZXMiOlsiQURNSU4iXSwiaWF0IjoxNTE2MjM5MDIyfQ.IfakCBAhIYgkUU12-bRK4r6Boo2FVuW1kd9POylK"}
{
  "sub": "1234567890",
  "name": "JONES, FRED",
  "family_name": "JONES",
  "given_name": "FRED",
  "roles": ["ADMIN"],
  "iat": 1516239022
}

Access Tokens

To return hardcoded access tokens, set accessTokens to an object containing valid tokens for each resource:

{clientType: "DUMMY",
 identityToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpPTkVTLCBGUkVEIiwiZmFtaWx5X25hbWUiOiJKT05FUyIsImdpdmVuX25hbWUiOiJGUkVEIiwicm9sZXMiOlsiQURNSU4iXSwiaWF0IjoxNTE2MjM5MDIyfQ.IfakCBAhIYgkUU12-bRK4r6Boo2FVuW1kd9POylK",
 accessTokens: {"ae33c32e-d2f2-4992-a4b2-51d03e7c8677": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1MDkyMzM0NzUsImV4cCI6MTU0MDc2OTQ3NSwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSJ9.SdoZIKv0cNZs6SS6EGTWVBRaeGm31rNztIC8jaLjg7o"}}

Identity (No Token)

To use a dummy identity with no actual token, set identity to an object containing the name, givenName, familyName, and roles properties:

{clientType: "DUMMY",
 identity: {name: "JONES, FRED",
            givenName: "FRED",
            familyName: "JONES",
            roles: ["Ninja", "Lawyer"]}}

Access Token Responses

To trigger various error conditions when retrieving access tokens, set accessTokenResponses to an object containing error responses for each resource:

{clientType: "DUMMY",
 identityToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpPTkVTLCBGUkVEIiwiZmFtaWx5X25hbWUiOiJKT05FUyIsImdpdmVuX25hbWUiOiJGUkVEIiwicm9sZXMiOlsiQURNSU4iXSwiaWF0IjoxNTE2MjM5MDIyfQ.IfakCBAhIYgkUU12-bRK4r6Boo2FVuW1kd9POylK",
 accessTokenResponses: {"ae33c32e-d2f2-4992-a4b2-51d03e7c8677": {"error": "dummy-error"}}}

Copyright and License

The MIT License (MIT)

Copyright (c) 2019 Michael Easter

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

0.0.11-alpha.1

6 years ago

0.0.11-alpha.0

6 years ago

0.0.10

6 years ago

0.0.9

6 years ago

0.0.8

7 years ago

0.0.7

7 years ago

0.0.6

7 years ago

0.0.5

7 years ago

0.0.5-alpha4

7 years ago

0.0.5-alpha3

7 years ago

0.0.5-alpha2

7 years ago

0.0.4

7 years ago

0.0.1

7 years ago

0.0.2

8 years ago