1.5.0 • Published 6 years ago

@tessin/authorize v1.5.0

Weekly downloads
-
License
MIT
Repository
-
Last release
6 years ago

Authorization Data Library

This library gets authorization data from an Azure storage account rather than Azure AD role assignments by simply fetching and collating a bunch of JSON documents.

Based on a token issued by Azure AD the library fetches the following JSON documents.

  1. {accountName}.blob.core.windows.net/groups/{oid}/{appId}/{scope}.json
  2. {accountName}.blob.core.windows.net/users/{oid}/{appId}/{scope}.json

All documents are fetched in parallel but the documents that apply to users take precedence and get after applied after any inherited data from security groups.

Example

import { createContext } from "@tessin/authorize"

const { validationContext, authorizationContext } = createContext(
  "524f5b50-2d73-4767-8d88-b2855c65350d", // tenant
  ["97851bc4-f874-4015-ac92-e509b319526a"], // allowed audience
  "AuthZ" // appId
)

// it is not possible to pass a token directly to authorization functions
// without first validating the token, only tokens issued by Azure AD for
// the intended audience pass this step, if validation fails it will return an error
// you can use `isValidToken` to test if the validation was successful
const validToken = await validationContext.validateToken(token)

// based on claims in the token the authorization data is read from the following URLs
//  {accountName}.blob.core.windows.net/groups/{oid}/Authz/roles.json
//  {accountName}.blob.core.windows.net/users/{oid}/Authz/roles.json
if (await authorizationContext.authorize(validToken, ["User.Read"])) {
  // ok
} else {
  // not ok
}

accountName is derived from tid tenant ID claim (see implementation of function accountName for details). For users oid corresponds to the oid object ID claim. For groups oid corresponds to the groups claim (one for each group membership). Also here is each group is an object ID.

The blob containers users and groups should be public for blob access. Authorization data is not sensitive and it's meant to be freely available for anyone to inspect.

Easy Auth

Easy Auth is a name given to the Azure AD app service authentication service. It is a nice feature that makes sure you are authenticated. This package has a subset of those features implemented so that you can have the same experience locally without being tied to the specifics of Azure App Service (we've had lots of issues with this when it comes to deployment slots and debugging). We've implemented the protocols ourself for increased transparency and understanding.

import http = require("http")

import { easyAuth, TokenValidationError } from "@tessin/authorize"

// to use easy auth you need a token validation context

const easyAuth_ = easyAuth(
  "524f5b50-2d73-4767-8d88-b2855c65350d", // tenant
  "c4cea48a-a8e9-4faa-9bb1-f60821c70b9f", // client ID
  tokenValidationContext
)

async function requestHandler(
  req: http.IncomingMessage,
  res: http.ServerResponse
) {
  const identity = await easyAuth_.login(req, res)
  if (identity === "pending") {
    return // the easy auth handler is processing the request
  }

  if (identity instanceof TokenValidationError) {
    // there was an error with the login process
    // details can be found on the identity object
    // which is actually a sub class of Error
    //
    // the error code will originate from the
    // Microsoft Identity Platform (aka Azure AD)
    // or the token validation code from this package
    // if the token doesn't pass validation
    //
    // you can handle this error anyway you like
    res.setHeader("content-type", "application/json")
    res.end(
      JSON.stringify({
        ok: false,
        ...identity.toJSON()
      })
    )
    return
  }

  console.log("ID token claims", identity.payload)

  // ...rest of request processing goes here
}

Application Manifest

Application registration should be done with accessTokenAcceptedVersion: 2 and need to be configured to use groupMembershipClaims: "SecurityGroup" as well as optionalClaims where accessToken include ipaddr for proper auditing.

{
  "accessTokenAcceptedVersion": 2,
  "groupMembershipClaims": "SecurityGroup",
  "optionalClaims": {
    "accessToken": [
      {
        "name": "ipaddr",
        "source": null,
        "essential": false,
        "additionalProperties": []
      }
    ]
  }
}

API details

accountName

Let's say your Azure AD Directory ID is 2f055a66-75dd-4eeb-bdad-69eb4f5a409b. Then the storage account name would be 69hf4b3cspe1iysssdm3sql7.

This mapping is done by parsing the version 4 random UUID as a hex string, omitting the constant version nibble 4 at index 12 (excluding hyphens) and then encoding the hex string in base 36.

users/groups

The blob containers users and groups are public (for read only). The reason for this is that the authorization data is not sensitive per se. And while we can use access tokens to protect the storage account the extra hassle is not worth it.

appId

Here appId, does not refer to a Azure AD application registration. appId can be any user friendly name assigned to your application.

For example Web-dev or Web-prod. The getAuthorization API doesn't mandate any rules what so ever. It's just a name identifying your application context.

scope

Scopes is just the name given to the type of authorization data. For example

GET /users/05585cb3-1307-48de-aee5-ac876f0b2424/Web-prod/roles.json
Host: 69hf4b3cspe1iysssdm3sql7.blob.core.windows.net
OK 200 HTTP/1.1

["User.Read"]

The well known roles scope is a JSON document containing an array of role memberships.

Limitations

References

1.5.0

6 years ago

1.4.0

6 years ago

1.3.0

6 years ago

1.2.0

6 years ago

1.1.1

6 years ago

1.1.0

6 years ago

1.0.3

6 years ago

1.0.2

6 years ago

1.0.1

6 years ago

1.0.0

6 years ago