Skip to content

Maskinporten Scopes

A scope represents a permission that a given consumer has access to. In Maskinporten, you can define scopes and grant other organizations access to these scopes.

As an API provider, you are fully responsible for defining the granularity of access and authorization associated with a given scope.

An external consumer that has been granted access to your scopes may then acquire an access_token using a Maskinporten client that belongs to their organization. Clients registered via NAIS belong to NAV and may only be used by NAV.

Spec

Example configuration:

nais.yaml
spec:
  maskinporten:
    enabled: true
    scopes:
      exposes:
        - name: "some.scope.read"
          enabled: true
          product: "arbeid"
          consumers:
            - orgno: "123456789"

See the NAIS manifest for the complete specification.

Network Connectivity

Maskinporten is an external service. The platform automatically configures outbound access to the Maskinporten hosts.

You do not have to explicitly configure outbound access to Maskinporten yourselves in GCP.

Runtime Variables & Credentials

Your application will automatically be injected with both environment variables and files at runtime. You can use whichever is most convenient for your application.

The files are available at the following path: /var/run/secrets/nais.io/maskinporten/

Name Description
MASKINPORTEN_WELL_KNOWN_URL The well-known URL for the metadata discovery document
MASKINPORTEN_ISSUER issuer from the metadata discovery document.
MASKINPORTEN_JWKS_URI jwks_uri from the metadata discovery document.

These variables are used when validating tokens issued by Maskinporten.

Getting Started

As an API provider, you will need to do three things:

  1. Define the scopes that you want to expose to other organizations
  2. Expose your application to the external consumers
  3. Validate tokens in requests from external consumers

1. Define Scopes

Declare all the scopes that you want to expose in your application's NAIS manifest:

nais.yaml
spec:
  maskinporten:
    enabled: true
    scopes:
      exposes:
        - name: "some.scope.read"
          enabled: true
          product: "arbeid"
        - name: "some.scope.write"
          enabled: true
          product: "arbeid"

Grant the external consumer access to the scopes by specifying their organization number:

nais.yaml
spec:
  maskinporten:
    enabled: true
    scopes:
      exposes:
        - name: "some.scope.read"
          ...
          consumers:
            - orgno: "123456789"

2. Expose Application

Expose your application to the consumer(s) at a publicly accessible ingress.

3. Validate Tokens

Verify incoming requests from the external consumer(s) by validating the Bearer token in the Authorization header.

Always validate the signature and standard time-related claims. Additionally, perform the following validations:

Issuer Validation

Validate that the iss claim has a value that is equal to either:

  1. the MASKINPORTEN_ISSUER environment variable, or
  2. the issuer property from the metadata discovery document. The document is found at the endpoint pointed to by the MASKINPORTEN_WELL_KNOWN_URL environment variable.

Scope Validation

Validate that the scope claim contains the expected scope(s). The scope claim is a string that contains a whitespace-separated list of scopes.

Continuing from the previous examples, you would validate that the scope claim contains at least one of:

  • nav:arbeid:some.scope.read or
  • nav:arbeid:some.scope.write

Audience Validation

The aud claim is not included by default in Maskinporten tokens and does not need to be validated. It is only included if the consumer has requested an audience-restricted token.

Only validate the aud claim if you want to require your consumers to use audience-restricted tokens. The expected audience value is up to you to define and must be communicated to your consumers. The value must be an absolute URI (such as https://some-provider.no or https://some-provider.no/api).

Signature Validation

Validate that the token is signed with a public key published at the JWKS endpoint. This endpoint URI can be found in one of two ways:

  1. the MASKINPORTEN_JWKS_URI environment variable, or
  2. the jwks_uri property from the metadata discovery document. The document is found at the endpoint pointed to by the MASKINPORTEN_WELL_KNOWN_URL environment variable.

Other Token Claims

Other claims may be present in the token. Validation of these claims is optional.

See the Access Token Reference in Maskinporten for a list of all claims.

Scope Naming

All scopes within Maskinporten consist of a prefix and a subscope:

scope := <prefix>:<subscope>

For example:

scope := nav:trygdeopplysninger

The prefix for all scopes provisioned through NAIS will always be nav.

A subscope should describe the resource to be exposed as accurately as possible. It consists of three parts; product, separator and name:

subscope := <product><separator><name>

The name may also be postfixed to separate between access levels. For instance, you could separate between write access:

name := trygdeopplysninger.write

...and read access:

name := trygdeopplysninger.read

Absence of a postfix should generally be treated as strictly read access.

If name does not contain any / (forward slash), the separator is set to : (colon).

For the following scope:

nais.yaml
spec:
  maskinporten:
    enabled: true
    scopes:
      exposes:
        - name: "some.scope.read"
          enabled: true
          product: "arbeid"
  • product is set to arbeid
  • name is set to some.scope.read

The subscope is then:

subscope := arbeid:some.scope.read

which results in the scope:

scope := nav:arbeid:some.scope.read

If name contains a / (forward slash), the separator is set to / (forward slash).

For the following scope:

nais.yaml
spec:
  maskinporten:
    enabled: true
    scopes:
      exposes:
        - name: "some/scope.read"
          enabled: true
          product: "arbeid"
  • product is set to arbeid
  • name is set to some/scope.read

The subscope is then:

subscope := arbeid/some/scope.read

which results in the scope:

scope := nav:arbeid/some/scope.read

Delegation of scopes

Delegation of scopes is not supported.

If you need a scope with delegation, please see IaC repository.