> TODO: description
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.
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() { ... }
}
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() { ... }
}
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.
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' } ]
}
}
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() { ... }
}
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
}
}
// 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)
}
}
// 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}`)
}