1
0
mirror of https://github.com/google/nomulus synced 2026-03-05 18:24:54 +00:00
Files
nomulus/docs/authentication-framework.md
gbrodman c52983fb61 Update some Nomulus documentation (#2970)
This doesn't update everything -- it leaves out some of the more
complicated changes (architecture, code-structure, configuration,
install, and proxy-setup). Those will require more complete rewrites, so
I'm punting them to a future PR.
2026-02-26 19:05:22 +00:00

9.3 KiB

Authentication framework

Nomulus performs authentication and authorization on a per-request basis. Each endpoint action defined has an @Action annotation with an auth attribute which determines the ways a request can authenticate itself, as well as which requests will be authorized to invoke the action.

Authentication and authorization properties

The auth attribute is an enumeration. Each value of the enumeration corresponds to a pair of properties:

  • the minimum authentication level which is authorized to run the action
  • the user policy for the action

Authentication Levels

There exist three levels of authentication level:

  • NONE: no authentication was found
  • APP: the request was authenticated, but no user was present
  • USER: the request was authenticated with a specific user

NONE and USER are fairly straightforward results (either no authentication was present, or a user was present), but APP is a bit of a special case. It exists for requests coming from service accounts, Cloud Scheduler, or the proxy -- requests which are authenticated but don't necessarily come from any one particular "user" per se. That being said, authorized users can manually run these tasks; it's just that service accounts can too.

Each action has a minimum request authentication level. Some actions (e.g. RDAP) are completely open to the public, and have a minimum level of NONE. Some require authentication but not necessarily a user, and have a minimum level of APP. And some cannot function properly without knowing the exact user, and have a minimum level of USER.

User policy

The user policy indicates what kind of user is authorized to execute the action. There are two possible values:

  • PUBLIC: an authenticated user is required, but any user will do (authorization is done at a later state)
  • ADMIN: there must be an authenticated user with admin privileges (this includes service accounts)

Note that the user policy applies only to the automatic checking done by the framework before invoking the action. The action itself may do more checking. For instance, the registrar console's main page has no authentication at all, and all requests are permitted. However, the first thing the code does is check whether a user was found. If not, it issues a redirect to the login page.

Likewise, other pages of the registrar console have a user policy of PUBLIC, meaning that any logged-in user can access the page. However, the code then looks up the user to make sure he or she is associated with a registrar.

Also note that the user policy only applies when there is actually a user. Some actions can be executed either by an admin user or by an internal request coming from a task queue, which will not have a defined user at all. So rather than determining the minimum user level, this setting should be thought of as determining the minimum level a user must have if there is a user at all. To require that there be a user, set the minimum authentication level to USER.

Allowed authentication and authorization values

There are three pairs of authentication level + user policy that are used in Nomulus (or even make sense). These are:

  • AUTH_PUBLIC: Allow all access and don't attempt to authenticate. This is used for completely public endpoints such as RDAP.
  • AUTH_PUBLIC_LOGGED_IN: Allow access only by users authenticated with some type of OAuth token. This allows all users (UserPolicy.PUBLIC) but requires that a particular user exists and is logged in (AuthLevel.USER). This is used primarily for the registrar console.
  • AUTH_ADMIN: Allow access only by admin users or internal requests (including Cloud Scheduler tasks). This is appropriate for actions that should only be accessed by someone trusted (as opposed to anyone with a Google login). This permits app-internal authentication (AuthLevel.APP) but if a user is present, it must be an admin (UserPolicy.ADMIN). This is used by many automated requests, as well as the proxy.

Action setting golden files

To make sure that the authentication and authorization settings are correct and expected for all actions, a unit test uses reflection to compare all defined actions for a specific service to a golden file containing the correct settings.

Each line in the file lists a path, the class that handles that path, the allowable HTTP methods (meaning GET and POST, as opposed to the authentication methods described above), the value of the automaticallyPrintOk attribute (not relevant for purposes of this document), and the two authentication and authorization settings described above. Whenever actions are added, or their attributes are modified, the golden file needs to be updated.

The golden file also serves as a convenient place to check out how things are set up. For instance, the backend actions are accessible to admins and internal requests only, the pubapi requests are open to the public, and console requests require an authenticated user.

Example

The EppTlsAction class handles EPP commands which arrive from the proxy via HTTP. Only admin users and internal requests should be allowed to execute this action, to avoid anyone on the Internet sending us random EPP commands. Further, the HTTP method needs to be POST, so that the EPP command is contained in the body rather than the URL itself (which could be logged). Therefore, the class definition looks like:


@Action(
    service = Action.Service.FRONTEND,
    path = "/_dr/epp",
    method = Method.POST,
    auth = Auth.AUTH_ADMIN)
public class EppTlsAction implements Runnable {
...

and the corresponding line in frontend_routing.txt (including the header line) is:

SERVICE   PATH             CLASS              METHODS     OK MIN  USER_POLICY
FRONTEND  /_dr/epp         EppTlsAction       POST        n  APP  ADMIN

Implementation

The code implementing the authentication and authorization framework is contained in the google.registry.request.auth package. The main method is authorize(), in RequestAuthenticator. This method takes the auth settings and an HTTP request, and tries to authenticate and authorize the request, returning the result of its attempts. Note that failed authorization (in which case authorize() returns Optional.absent()) is different from the case where nothing can be authenticated, but the action does not require any; in that case, authorize() succeeds, returning the special result AuthResult.NOT_AUTHENTICATED.

The ultimate caller of authorize() is google.registry.request.RequestHandler, which is responsible for routing incoming HTTP requests to the appropriate action. After determining the appropriate action, and making sure that the incoming HTTP method is appropriate for the action, it calls authorize(), and rejects the request if authorization fails.

Authentication methods

Nomulus requests are authenticated via OIDC token authentication, though these tokens can be created and validated in two ways. In each case, the authentication mechanism converts an HTTP request to an authentication result, which consists of an authentication level, a possible user object, and a possible service account email.

IapOidcAuthenticationMechanism

Most requests, e.g. the registrar console or Nomulus CLI requests) are routed through GCP's Identity-Aware Proxy. This forces the user to log in to some GAIA account (specifically, one that is given access to the project). We attempt to validate a provided IAP OIDC token with the IAP issuer URL (https://cloud.google.com/iap) and the proper IAP audience (/projects/{projectId}/global/backendServices/{serviceId}), where projectId refers to the GCP project, and serviceId refers to the service ID retrievable from the IAP configuration page. Ideally, this service ID corresponds to the HTTPS load balancer that distributes requests to the GKE pods.

Note: the local Nomulus CLI's LoginCommand uses a special-case form of this where it saves long-lived IAP credentials locally.

RegularOidcAuthenticationMechanism

Service account requests ( e.g. Cloud Scheduler jobs) or requests coming through the proxy use a non-IAP OIDC token provided by the caller. These requests have a different issuer URL ( https://accounts.google.com) and use the fairly standard OAuth bearer token architecture -- an Authorization HTTP header of the form "Bearer: XXXX".

Configuration

The auth block of the configuration requires two fields: * allowedServiceAccountEmails is the list of service accounts that should be allowed to run tasks when internally authenticated. This will likely include whatever service account runs Nomulus in Google Kubernetes Engine, as well as the Cloud Scheduler service account. * oauthClientId is the OAuth client ID associated with IAP. This is retrievable from the Clients page of GCP after enabling the Identity-Aware Proxy. It should look something like someNumbers-someNumbersAndLetters.apps.googleusercontent.com