@urbaninfrastructure/api-auth-sdk
TypeScript icon, indicating that this package has built-in type declarations

1.2.6 • Public • Published

@urbaninfrastructure/api-auth-sdk

> TODO: description

External request ACL validation

Import from /nestjs submodule in nestjs project, and from /express in others.

In frontend/web projects pure functions and interfaces can be imported from root module.

NestJS

Split ACL

import { AuthorizedResource, AuthorizedAction } from '@urbaninfrastructure/api-auth-sdk/nestjs'

// Decorate a controller, preferably using typed enums
@AuthorizedResource('fleet', 'vehicle')
@Controller()
class Controller {
    @Get('/edit')
    // Decorate a function with the allowed action
    @AuthorizedAction('update')
    async editResource() { ... }

    @Get('/other/edit')
    // Nested resource, first argument is nested resource, second is action
    @AuthorizedAction('other', 'update')
    async editOtherResource() { ... }
}

Object ACL

import { Authorized } from '@urbaninfrastructure/api-auth-sdk/nestjs'

@Controller()
class Controller {
    @Get('/edit')
    // Decorate function with full acl definition
    @Authorized({ product: 'fleet', resource: 'vehicle', action: 'update' })
    async editResource() { ... }
}

Object ACL, combinatorics

Arbitrary combinations using AclCombination interface can be supplied in order to validate complex acl definitions

@Authorized({
    and: [
        { is: { product: 'fleet', resource: 'vehicle', action: 'update' } },
        { is: { product: 'fleet', resource: 'trip', action: 'any' } },
        {
            not: [
                { is: toAcl('fleet', 'user', 'create') }
            ]
        }
    ]
})

See the @Authorized decorator function docs too.

Get authenticated administrator and their permissions

import {
    AclCandidate, Admin, Authorized, Permissions
} from '@urbaninfrastructure/api-auth-sdk/nestjs'

@Controller()
class Controller {
    @Get('/path')
    @Authorized({ product: 'fleet', resource: 'vehicle', action: 'any' })
    async pathHandler(
        @Admin() admin: AdministratorEntity,
        @Permissions() permissions: AclCandidate[],
    ) {
        console.log(admin)
        // { id: 'adm_1...', email: 'my@email.com', ... }
        console.log(permissions)
        // [ { product: 'fleet', owner: 'oslobysykkel', resource: 'vehicle', action: 'any' } ]
    }
}

AclOwner decorator

If the Owner (system id) isn't in a well-known location in the request (systemId or metaSystemId), use @AuthorizedOwner() to help locate it. Can also be used to specify "any" owner.

import {
    AuthorizedOwner, AuthorizedAction, AuthorizedResource
} from '@urbaninfrastructure/api-auth-sdk/nestjs'

@AuthorizedResource('vehicle')
@Controller
class Controller {
    @Get('/path')
    @AuthorizedOwner((req) => req.params.ownerId)
    @AuthorizedAction('read')
    async readVehicle() { ... }
}

Custom / express/sequelize

Provided middleware

import { reqHasAcl, toAcl, expressAuthorized } from '@urbaninfrastructure/api-auth-sdk/express'

router.use(expressAuthorized)

function hasPermission(req: Request, owner: string, resource: AclResource, action: AclAction): bool {
    return reqHasAcl(req, toAcl('fleet', owner, resource, action))
}

function onRequest(req: Request) {
    if (!hasPermission(req, req.systemId, 'vehicle', 'read')) {
        return null
    }
}

Custom

// Server (f.ex. core):
import {
    authenticateExternalRequest, authorizeAdministrator, toAcl
} from '@urbaninfrastructure/api-auth-sdk/express'

function onRequest(req: Request) {
    // Make sure req has cookies/headers (ie. express's cookie middleware)
    /// authenticateExternalRequest throws UnauthorizedError if token is invalid
    const jwtToken = authenticateExternalRequest(req)
    const result = authorizeAdministrator(jwtToken.sub, toAcl('fleet', systemId, 'vehicle', 'read'))
    // alternative:
    const result = authorizeAdministrator(jwtToken.sub, { product: 'fleet', owner: systemId, resource: 'vehicle', action: 'read' })
    // or an AclCombination:
    const result = authorizeAdministrator(jwtToken.sub, { and: [ { is: toAcl(...) }, { is: toAcl(...) } ] })

    if (!result.allowed) {
        throw new Error('unauthorized', result.missedAcls)
    }
}

Internal request usage

// Client:
import { getRequestToken, ApiJwtAudience } from '@urbaninfrastructure/api-auth-sdk/nestjs'
const token = getRequestToken(ApiJwtAudience.CORE)
doRequest(coreUrl, headers: { Authorization: `Bearer ${token}` })

// Server (nestjs)
import { InternalAuth } from '@urbaninfrastructure/api-auth-sdk/nestjs'

@Module({
    providers: [
        {
            provide: APP_GUARD,
            useClass: InternalAuth,
        },
    ],
})
export class AppModule {}

// Server (f.ex. in core):
import { authenticateInternalRequest } from '@urbaninfrastructure/api-auth-sdk/express'

function onRequest(req: Request) {
    const authMetadata = authenticateInternalRequest(req)
    console.log(`Requesting: ${authMetadata.requestingClient}`)
    console.log(`System id: ${authMetadata.urbanSharingSystemId}`)
}

Readme

Keywords

none

Package Sidebar

Install

npm i @urbaninfrastructure/api-auth-sdk

Weekly Downloads

136

Version

1.2.6

License

ISC

Unpacked Size

146 kB

Total Files

194

Last publish

Collaborators

  • ghassenda
  • queses
  • romanyankovsky
  • yngve
  • norgepaul
  • p.fed
  • anna-maksimenko
  • tech-ops-urbansharing