Table of Contents

Tools : Portals

Introduction

Administration of the Portals collection - the hostname allowlist that decides which origins may authenticate at all, and which WebAuthn relying party (rpId) each of them belongs to. See webauthn for what a relying party and a related origin are.

All four endpoints require access level ADMIN (security.token.minAdmin). Every write invalidates the in-process portal cache immediately, rather than waiting for its normal 60 second refresh.

List

URL https://api.telecomx.dk/tools/portals
Method GET
Header Authorization String Bearer token

Response

{ portals: [ /* full Portal documents, sorted by hostname */ ] }

Create

URL https://api.telecomx.dk/tools/portals
Method POST
Header Authorization String Bearer token
Body JSON object
hostname String Required, 3-120 characters, lowercased, ^[a-z0-9.-]+(:\d+)?$. Host part of the Origin header this portal answers to, port included when non-default
customer Id, null Optional. Owning reseller; omit or null for a Telecom X-owned portal
name String Required, 1-60 characters. Shown as the WebAuthn relying party name in the OS prompt
rpId String Required, 3-120 characters, lowercased, ^[a-z0-9.-]+$. The relying party this hostname authenticates against
relatedOrigin Boolean Required. True to publish this hostname in the anchor's /.well-known/webauthn
enrollUrl String Required, a URL, max 300 characters. Enrollment link template with {token} and {lang} placeholders
state String Required. ACTIVE or DISABLED

Body example

{
  hostname: 'admin.xint.dk',
  customer: null,
  name: 'Xint Danmark',
  rpId: 'xint.dk',
  relatedOrigin: false,
  enrollUrl: 'https://admin.xint.dk/#/passkey?t={token}&l={lang}',
  state: 'ACTIVE'
}

Response

{ success: true, portal: { /* the stored Portal document, including _id, createdAt, updatedAt */ } }

Write-time validation

Two checks run on every create and update, against the full resulting set of portals - not just the one being written:

  1. Relying party reachability. A browser only accepts an rpId that is the hostname itself,

or a parent domain of it - or, failing that, the hostname must be published as a related

  origin. Concretely:
  * ''rpId'' with no ''.'' - a single label such as ''dk'' or ''com'' - is refused outright as a
    public suffix that no browser would ever accept. ''localhost'' is the sole exception, for
    development.
  * Otherwise, the write is refused unless ''hostname'' (without its port) equals ''rpId'', ends
    with ''.<rpId>'', or ''relatedOrigin'' is true.
- **Related-origin label budget.** If this write's ''relatedOrigin: true'' portals, combined with
  every other ''ACTIVE'' related-origin portal, would use more than 5 unique registrable-domain
  labels (excluding the anchor's own ''telecomx'' label), the write is refused - see
  [[api:auth:webauthn]] for why 5 is the limit.

Errors

Error code Message Description
409 hostname A portal with this hostname already exists
422 rpId rpId is a public suffix (e.g. dk), or cannot serve hostname without relatedOrigin: true
422 relatedOrigin This write would exceed the 5-label related-origin budget

Update

Same body and validation as create, applied to an existing portal.

URL https://api.telecomx.dk/tools/portals/<portal-id>
Method POST
Header Authorization String Bearer token
Body JSON object Same shape as create

Response

{ success: true, portal: { /* the updated Portal document */ } }

Errors

Error code Message Description
404 portal Portal does not exist
409 hostname The new hostname collides with a different, existing portal
422 rpId rpId is a public suffix, or cannot serve hostname without relatedOrigin: true
422 relatedOrigin This write would exceed the 5-label related-origin budget

Delete

URL https://api.telecomx.dk/tools/portals/<portal-id>
Method DELETE
Header Authorization String Bearer token

Response

{ success: true }

Errors

Error code Message Description
404 portal Portal does not exist