Device Code Planning Doc

High Level Overview

  • CLI asks Authenticator for a device code
  • Authenticator gives back a device_code, a user_code, an expiration, and a verification_url
  • The CLI tells the user, go to this URL and enter this code
  • While the user does this, the CLI polls the token endpoint with device_code
  • Once the user finishes, the token endpoint return the Tapis tokens

Required Endpoints For Device Code

  • /v3/oauth2/hello

    • operationId: hello
    • Method: GET
    • Checks if we can talk to the auth service
    • Not part of the login flow itself, just a connectivity check
  • v3/oauth2/device/code

    • operationId: generate_device_code
    • Method: Post
    • First step of the device code by asking the auth server to start a device login session
    • Requires the user to post with the NewDeviceCode scheme
      • The only thing that this requires is client_id
    • Returns a DeviceCodeResponse scheme that gives the user
      • device_code, user_code, client_id, expires_in, verification_url
  • /v3/oauth2/tokens

    • operationId: create_token
    • Method: POST
    • Second step of the device code flow
    • Used by the CLI to exchange or poll with the device flow information until the auth server returns tokens
    • Requires the user to send a request body using the NewToken schema
    • For the device code flow, the main fields the CLI cares about are:
      • client_id
      • device_code
      • grant_type
    • Returns a TokenResponse schema
    • This is the endpoint that returns the Tapis JWT and possibly a refresh token

Summarized OpenAPI Spec

openapi: "3.0.2"

info:
  title: "Authenticator"
  description: "REST API and web server providing authentication for a Tapis v3 instance."
  version: "1"
  termsOfService: "https://tapis-project.org"
  contact:
    name: "Authenticator"
    url: "https://tapis-project.org"
    email: "cicsupport@tacc.utexas.edu"
  license:
    name: "BSD 3"
    url: "https://github.com/tapis-project/authenticator"

servers:
- url: http://localhost:5000
  description: Local Development
- url: http://{tenant_id_url}.develop.tapis.io
  description: Tapis Develop instance
  variables:
    tenant_id_url:
      default: dev
      description: The tenant_id associated with the request.
- url: /
  description: catch-all server definition for other Tapis instances.


paths:
  /v3/oauth2/hello:
    get:
      tags:
        - Health Check
      description: Logged connectivity test. No authorization required.
      operationId: hello
      responses:
        '200':
          description: Message received.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BasicResponse'
        '500':
          description: Server error.

  /v3/oauth2/device/code:
    post:
      tags:
        - Tokens
      summary: Generate a device code.
      description: Generate a device code; this is the first step in the device_code grant type. See the OAuth2 documentation for details.
      operationId: generate_device_code
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewDeviceCode'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/BasicResponse'
                properties:
                  result:
                    $ref: '#/components/schemas/DeviceCodeResposne'

  /v3/oauth2/tokens:
    post:
      tags:
        - Tokens
      summary: Generate a Tapis JWT
      description: Generate a Tapis JWT using some OAuth2 grant type. Typically, a request to this endpoint is the last step in the token generation process. The fields required in the request payload depend on the grant type being used (see details below).
      operationId: create_token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewToken'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/NewToken'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/BasicResponse'
                properties:
                  result:
                    $ref: '#/components/schemas/TokenResponse'


  schemas:
    BasicResponse:
      type: object
      properties:
        version:
          type: string
          description: Version of the API
        message:
          type: string
          description: Brief description of the response
        status:
          type: string
          description: Whether the request was a success or failure.
          enum: [success, failure]
        metadata:
          type: object
          description: Metadata about the result object, including pagination information

    NewDeviceCode:
      type: object
      required: [client_id]
      properties:
        client_id:
          type: string
          description: The client_id requesting the device code.

    DeviceCodeResposne:
      type: object
      required: [device_code, user_code, client_id, expires_in, verification_uri]
      properties:
        device_code:
          type: string
          description: The device code generated for the client
        user_code:
          type: string
          description: The user code generated for the client
        client_id:
          type: string
          description: The client_id of the client
        expires_in:
          type: string
          description: The expiration for the user code
        verification_uri:
          type: string
          description: The url the user should go to to enter their user code

    NewToken:
      type: object
      properties:
        username:
          type: string
          description: The username being authenticated (for password grant).
        password:
          type: string
          description: The password assoicated with the username being authenticated (for password grant).
        client_id:
          type: string
          description: The client_id being authenticated (for device_code grant).
        client_key:
          type: string
          description: The client_key being authenticated (optional for authorization_code grant).
        grant_type:
          type: string
          description: The OAuth2 grant type being used; either password, authorization_code or refresh_token.
        redirect_uri:
          type: string
          description: The client's redirect URI (for authorization_code grant).
        code:
          type: string
          description: The authorization code associated with the request (for authorization_code grant).
        device_code:
          type: string
          description: The device code associated with the request (for device_code grant)
        refresh_token:
          type: string
          description: The refresh token associated with the request (for refresh_token grant).

    TokenResponse:
      type: object
      required: [access_token]
      properties:
        access_token:
          type: object
          description: A Tapis access token object.
          properties:
            access_token:
              type: string
              description: The actual access token as a JWT
            id_token:
              type: string
              description: The actual access token as a JWT
            expires_at:
              type: string
              description: The time, as a string in UTC, when the token expires.
            expires_in:
              type: integer
              description: The amount of time, in seconds, when the token will expire.
            jti:
              type: string
              description: Unique identifier for the token
        refresh_token:
          type: object
          description: A Tapis refresh token object.
          properties:
            refresh_token:
              type: string
              description: The actual refresh token as a JWT
            expires_at:
              type: string
              description: The time, as a string in UTC, when the token expires.
            expires_in:
              type: integer
              description: The amount of time, in seconds, when the token will expire.
            jti:
              type: string
              description: Unique identifier for the token