NUS Federated Identity Service (VAFS)

The National University of Singapore's own identity provider, and the only substantial machine-readable API surface the institution operates itself. Publishes an OpenID Connect discovery document, a JSON Web Key Set, and signed SAML 2.0 / WS-Federation metadata, all anonymously readable. Fronts OAuth 2.0 authorization, token, userinfo, device-code and logout endpoints. Relying parties are registered by NUS Information Technology; there is no public client registration and no self-service path.

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/identity"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no email required.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

nus-identity-openapi.yml Raw ↑
openapi: 3.0.3
info:
  title: NUS Identity Provider API (vafs.nus.edu.sg)
  version: '2026-08-19'
  description: |
    The National University of Singapore operates its own federated identity service at
    `vafs.nus.edu.sg` ("NUS VAFS" — the university's Active Directory Federation Services
    deployment). It is the one substantial machine-readable, institution-operated API surface
    NUS publishes to the open internet, and it is the front door to essentially every gated
    NUS system.

    The host is unambiguously NUS's own engineering, not a vendor's:

      * `vafs.nus.edu.sg` resolves inside NUS's own address space (137.132.21.18).
      * Its TLS certificate is an Extended Validation certificate issued to
        `jurisdictionC=SG, businessCategory=Private Organization, serialNumber=200604346E,
        O=National University of Singapore, CN=vafs.nus.edu.sg` — NUS is the validated
        organization, not a hosting provider.
      * The SAML entityID it asserts is `https://vafs.nus.edu.sg/adfs/services/trust`.

    This document is DERIVED — every path, parameter, scope and response below was read out of
    the institution's own live OpenID Connect discovery document at
    `https://vafs.nus.edu.sg/adfs/.well-known/openid-configuration` and its JWKS at
    `https://vafs.nus.edu.sg/adfs/discovery/keys`, both fetched 2026-08-19. NUS does not itself
    publish an OpenAPI description; nothing here is invented, but the OpenAPI packaging is
    API Evangelist's, not the university's.

    The service speaks OpenID Connect 1.0 / OAuth 2.0, SAML 2.0, WS-Federation and WS-Trust
    from the same endpoint family. The SAML half is described in
    `identity-federation/nus-identity-federation.yml`.
  contact:
    name: NUS Information Technology
    url: https://nusit.nus.edu.sg/
  termsOfService: https://nus.edu.sg/ormc/personal-data-protection/nus-privacy-notice
  x-operator: institution
  x-operator-evidence:
    - 'TLS certificate subject O=National University of Singapore (DigiCert EV RSA CA G2)'
    - 'A record 137.132.21.18 within NUS-assigned address space'
    - 'SAML entityID https://vafs.nus.edu.sg/adfs/services/trust'
  x-method: derived
  x-source: https://vafs.nus.edu.sg/adfs/.well-known/openid-configuration
  x-generated: '2026-08-19'
servers:
  - url: https://vafs.nus.edu.sg
    description: NUS federated identity service (production)
tags:
  - name: Discovery
    description: Machine-readable metadata describing the identity service and its signing keys.
  - name: Authorization
    description: OAuth 2.0 / OpenID Connect authorization and token issuance.
  - name: Session
    description: End-user claims and session termination.
paths:
  /adfs/.well-known/openid-configuration:
    get:
      tags: [Discovery]
      operationId: getOpenIdConfiguration
      summary: OpenID Connect discovery document
      description: >-
        Returns the OpenID Provider metadata for the NUS identity service, per
        OpenID Connect Discovery 1.0. Publicly readable, no authentication.
      security: []
      responses:
        '200':
          description: OpenID Provider metadata.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OpenIdConfiguration'
  /adfs/discovery/keys:
    get:
      tags: [Discovery]
      operationId: getJwks
      summary: JSON Web Key Set
      description: >-
        Returns the RSA public keys used to verify ID tokens and access tokens issued by
        this provider (RFC 7517). Publicly readable, no authentication.
      security: []
      responses:
        '200':
          description: JWKS document.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Jwks'
  /adfs/oauth2/authorize/:
    get:
      tags: [Authorization]
      operationId: authorize
      summary: OAuth 2.0 / OIDC authorization endpoint
      description: >-
        Begins an interactive authorization request. The user authenticates against NUS
        credentials (and MFA where the relying party requires it) and is redirected back to
        the client with a code, an id_token, or both, per `response_type`.
      security: []
      parameters:
        - name: client_id
          in: query
          required: true
          description: Identifier of the relying party registered with NUS IT.
          schema: { type: string }
        - name: response_type
          in: query
          required: true
          description: One of the provider's advertised response types.
          schema:
            type: string
            enum: [code, id_token, code id_token, id_token token, code token, code id_token token]
        - name: redirect_uri
          in: query
          required: true
          schema: { type: string, format: uri }
        - name: scope
          in: query
          required: true
          description: Space-delimited subset of the provider's advertised scopes.
          schema: { type: string, example: 'openid profile email' }
        - name: response_mode
          in: query
          required: false
          schema: { type: string, enum: [query, fragment, form_post] }
        - name: state
          in: query
          required: false
          schema: { type: string }
        - name: nonce
          in: query
          required: false
          schema: { type: string }
        - name: resource
          in: query
          required: false
          description: ADFS relying-party resource identifier.
          schema: { type: string }
      responses:
        '302':
          description: Redirect back to `redirect_uri` carrying the authorization response.
          headers:
            Location:
              schema: { type: string, format: uri }
        '400':
          description: Invalid or unregistered authorization request.
  /adfs/oauth2/token/:
    post:
      tags: [Authorization]
      operationId: token
      summary: OAuth 2.0 token endpoint
      description: >-
        Exchanges a grant for tokens. The provider advertises the authorization_code,
        refresh_token, client_credentials, jwt-bearer, implicit, password, device_code and
        srv_challenge grant types.
      security:
        - clientSecretBasic: []
        - clientSecretPost: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [grant_type]
              properties:
                grant_type:
                  type: string
                  enum:
                    - authorization_code
                    - refresh_token
                    - client_credentials
                    - 'urn:ietf:params:oauth:grant-type:jwt-bearer'
                    - implicit
                    - password
                    - srv_challenge
                    - 'urn:ietf:params:oauth:grant-type:device_code'
                    - device_code
                code: { type: string }
                refresh_token: { type: string }
                redirect_uri: { type: string, format: uri }
                client_id: { type: string }
                client_secret: { type: string }
                client_assertion_type: { type: string }
                client_assertion: { type: string }
                scope: { type: string }
      responses:
        '200':
          description: Token response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
        '400':
          description: OAuth 2.0 error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthError'
  /adfs/oauth2/devicecode:
    post:
      tags: [Authorization]
      operationId: deviceAuthorization
      summary: Device authorization endpoint
      description: >-
        RFC 8628 device authorization grant, for input-constrained clients on the NUS network.
      security: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [client_id]
              properties:
                client_id: { type: string }
                scope: { type: string }
      responses:
        '200':
          description: Device code, user code and verification URI.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeviceCodeResponse'
        '400':
          description: OAuth 2.0 error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthError'
  /adfs/userinfo:
    get:
      tags: [Session]
      operationId: userinfo
      summary: UserInfo endpoint
      description: >-
        Returns claims about the authenticated NUS end user. Requires a bearer access token
        issued with the `openid` scope. Subject identifiers are pairwise.
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Claims for the authenticated subject.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserInfo'
        '401':
          description: Missing, expired or invalid access token.
  /adfs/oauth2/logout:
    get:
      tags: [Session]
      operationId: endSession
      summary: End session / front-channel logout endpoint
      description: >-
        Terminates the user's session at the provider. The provider advertises
        front-channel logout with session support.
      security: []
      parameters:
        - name: post_logout_redirect_uri
          in: query
          required: false
          schema: { type: string, format: uri }
        - name: id_token_hint
          in: query
          required: false
          schema: { type: string }
      responses:
        '302':
          description: Redirect after session termination.
  /FederationMetadata/2007-06/FederationMetadata.xml:
    get:
      tags: [Discovery]
      operationId: getFederationMetadata
      summary: SAML 2.0 / WS-Federation metadata
      description: >-
        Returns the signed SAML 2.0 metadata document for entityID
        `https://vafs.nus.edu.sg/adfs/services/trust`, carrying both an IDPSSODescriptor and
        an SPSSODescriptor, four SingleLogoutService bindings, and 76 declared claim types.
        Publicly readable, no authentication.
      security: []
      responses:
        '200':
          description: Signed SAML 2.0 EntityDescriptor.
          content:
            application/samlmetadata+xml:
              schema: { type: string, format: xml }
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
    clientSecretBasic:
      type: http
      scheme: basic
      description: client_secret_basic client authentication.
    clientSecretPost:
      type: apiKey
      in: header
      name: Authorization
      description: >-
        Placeholder for client_secret_post; the provider also advertises private_key_jwt and
        windows_client_authentication.
    oidc:
      type: openIdConnect
      openIdConnectUrl: https://vafs.nus.edu.sg/adfs/.well-known/openid-configuration
  schemas:
    OpenIdConfiguration:
      type: object
      description: OpenID Provider metadata as returned by the live NUS discovery endpoint.
      required: [issuer, authorization_endpoint, token_endpoint, jwks_uri]
      properties:
        issuer: { type: string, example: 'https://vafs.nus.edu.sg/adfs' }
        authorization_endpoint: { type: string, example: 'https://vafs.nus.edu.sg/adfs/oauth2/authorize/' }
        token_endpoint: { type: string, example: 'https://vafs.nus.edu.sg/adfs/oauth2/token/' }
        jwks_uri: { type: string, example: 'https://vafs.nus.edu.sg/adfs/discovery/keys' }
        userinfo_endpoint: { type: string, example: 'https://vafs.nus.edu.sg/adfs/userinfo' }
        end_session_endpoint: { type: string, example: 'https://vafs.nus.edu.sg/adfs/oauth2/logout' }
        device_authorization_endpoint: { type: string, example: 'https://vafs.nus.edu.sg/adfs/oauth2/devicecode' }
        access_token_issuer: { type: string, example: 'https://vafs.nus.edu.sg/adfs/services/trust' }
        scopes_supported:
          type: array
          items: { type: string }
        response_types_supported:
          type: array
          items: { type: string }
        grant_types_supported:
          type: array
          items: { type: string }
        subject_types_supported:
          type: array
          items: { type: string, enum: [pairwise] }
        id_token_signing_alg_values_supported:
          type: array
          items: { type: string }
        token_endpoint_auth_methods_supported:
          type: array
          items: { type: string }
        claims_supported:
          type: array
          items: { type: string }
        frontchannel_logout_supported: { type: boolean }
        frontchannel_logout_session_supported: { type: boolean }
    Jwks:
      type: object
      required: [keys]
      properties:
        keys:
          type: array
          items:
            type: object
            required: [kty, use, alg, kid, n, e]
            properties:
              kty: { type: string, example: RSA }
              use: { type: string, example: sig }
              alg: { type: string, example: RS256 }
              kid: { type: string }
              x5t: { type: string }
              n: { type: string, description: RSA modulus, base64url. }
              e: { type: string, example: AQAB }
              x5c:
                type: array
                items: { type: string }
    TokenResponse:
      type: object
      required: [access_token, token_type]
      properties:
        access_token: { type: string }
        token_type: { type: string, example: bearer }
        expires_in: { type: integer, example: 3600 }
        refresh_token: { type: string }
        refresh_token_expires_in: { type: integer }
        id_token: { type: string, description: 'JWT signed with RS256.' }
        scope: { type: string }
    DeviceCodeResponse:
      type: object
      required: [device_code, user_code, verification_uri]
      properties:
        device_code: { type: string }
        user_code: { type: string }
        verification_uri: { type: string, format: uri }
        expires_in: { type: integer }
        interval: { type: integer }
    UserInfo:
      type: object
      description: >-
        Claims the provider advertises in `claims_supported`. NUS issues pairwise subject
        identifiers, so `sub` is per-client and not a stable cross-client campus identifier.
      properties:
        sub: { type: string }
        upn: { type: string, description: 'User principal name, e.g. e0123456@u.nus.edu' }
        unique_name: { type: string }
        aud: { type: string }
        iss: { type: string }
        iat: { type: integer }
        exp: { type: integer }
        nbf: { type: integer }
        auth_time: { type: integer }
        mfa_auth_time: { type: integer }
        nonce: { type: string }
        at_hash: { type: string }
        c_hash: { type: string }
        sid: { type: string }
        pwd_url: { type: string, format: uri }
        pwd_exp: { type: integer }
    OAuthError:
      type: object
      required: [error]
      properties:
        error: { type: string, example: invalid_grant }
        error_description: { type: string }