Integration Developer Documentation

Table of Contents

Introduction

The audience of this document is developers who want to integrate with SecureX, Threat Response or more generally with IROH1. IROH is the name of the API behind both SecureX and Threat Response.

You generally don’t need to read the full document but only the sections that are meaningful for you.

Call the API / JWT

All the IROH1 API Calls use a JWT as a means to authenticate users.

Here is an example of decoded IROH1 JWT:

{
    "iss": "IROH Auth NAM",
    "sub": "idb-amp:13375ee9-2e3a-4e1b-977d-961facb5fd84",
    "exp": 1556693276,
    "nbf": 1556606576,
    "iat": 1556606876,
    "jti": "r93c7fa55-7a66-4708-824b-bd697d5d1211",
    "email": "dev.null@cisco.com",
    "https://schemas.cisco.com/iroh/identity/claims/version":
    "v1.20.0",
    "https://schemas.cisco.com/iroh/identity/claims/user/id":
    "idb-amp:13375ee9-2e3a-4e1b-977d-961facb5fd84",
    "https://schemas.cisco.com/iroh/identity/claims/user/email":
    "dev.null@cisco.com",
    "https://schemas.cisco.com/iroh/identity/claims/user/nick":
    "Dev Null",
    "https://schemas.cisco.com/iroh/identity/claims/user/idp/id":
    "idb-amp",
    "https://schemas.cisco.com/iroh/identity/claims/user/idp/org-id":
    "13375cf9-561c-4958-0000-6d84b7ef09d4",
    "https://schemas.cisco.com/iroh/identity/claims/user/idp/user-id":
    "13375ee9-2e3a-4e1b-977d-961facb5fd84",
    "https://schemas.cisco.com/iroh/identity/claims/org/id":
    "13375cf9-561c-4958-0000-6d84b7ef09d4",
    "https://schemas.cisco.com/iroh/identity/claims/org/name":
    "IROH Testing",
    "https://schemas.cisco.com/iroh/identity/claims/scopes":
    [ "iroh-admin",
      "integration",
      "private-intel",
      "admin",
      "profile",
      "inspect",
      "iroh-master",
      "iroh-auth",
      "sse",
      "users",
      "cisco",
      "casebook",
      "orbital",
      "enrich",
      "oauth",
      "global-intel",
      "account-activation",
      "collect",
      "response",
      "ui-settings" ],
    "https://schemas.cisco.com/iroh/identity/claims/oauth/client/id":
    "iroh-ui",
    "https://schemas.cisco.com/iroh/identity/claims/oauth/client/name":
    "iroh-ui",
    "https://schemas.cisco.com/iroh/identity/claims/oauth/kind":
    "session-token",
}

JWT Claims

Standard Claims

IROH1 Specific Claims

Those claims start with https://schemas.cisco.com/iroh/identity/claims/ to prevent any collision.

  • version

    The JWT version. IROH1 APIs check the version is similar otherwise returns a 401.

  • user/id

    The user’s unique identifier across IROH1.

  • user/email (optional)

    The user email if known. This claim can be empty and is just informational. There is no email verification directly done by IROH1.

  • user/name (optional)

    The user name if known. This claim can be empty and is just informational. IROH1 does not check the real user name of our users.

  • user/nick (optional)

    The user nickname if known. This claim can be empty and is just informational.

  • org/id

    The organization unique identifier across IROH1.

  • org/name (optional)

    The organization name if known. This claim can be empty and is just informational. IROH1 does not check the real org name of our users.

  • scopes

    The list of scopes granted by the JWT. See Authorization Access / Scopes for more information about them.

  • user/idp/id

    IROH1 uses 3rd party Identity Provider to authenticate users. More precisely IROH1 communicates with Cisco Security (AMP) or Threatgrid for getting the Identities.

  • user/idp/org-id

    The org-id provided by the Identity Provider. It is generally not equal to the IROH1 org id.

  • user/idp/user-id

    The user-id provided by the Identity Provider. It is generally not equal to the IROH1 user id.

  • oauth/client/id

    The JWT is generally generated via an OAuth2 workflow. The OAuth2 client id involved in the JWT generation.

  • oauth/client/name

    The JWT is generally generated via an OAuth2 workflow. This is the client name involved in the JWT generation.

  • oauth/kind

    This can be either “session-token”, “access-token” or “refresh-token”.

    • Session token are the one generated during a login workflow
    • Access token are the one generated during any OAuth2 workflow
    • Refresh token are reserved to refresh token generated during OAuth2 Auth Code Credential Grant.

Data Model

To work with IROH1 API you need to understand some concepts behind the users. The User data model follow this hierarchy:

  1. At the top level we have Orgs,
  2. Each Org can have many Users,
  3. Each User can have many Clients.

When you will need to deal either directly with the API or to use the JWT you will be faced with the same set of fields.

Retrieve user & org data

If you login and make an HTTP GET call to /iroh/profile/whoami you will get a response with the following format

{"user": ...,
 "org": ...}

The user field points to a User object, and the org field points to an Org object.

Each entity is identified with a unique id that is generally not human friendly. All entities have other field(s) that should be displayed to the end user.

Org Model

Here is a JSON example of an Org:

{"id":"some-unique-org-id",
 "name":"Cisco",
 "additional-scopes":["super" "sse"],
 "allow-all-role-to-login":true,
 "scim-status":"activated",
 "enabled?":true,
 "settings":{"allow-all-roles-to-login":false}}

Here is an exhaustive list of fields of the Org model with their possible values and descriptions:

  • id: String; Mandatory; A unique identifier for IROH1, not user friendly
  • name: String; Org name, if present should be human friendly.
  • additional-scopes: [String]; A list of additional scope(s) provided by IROH1 masters2. For more information about scopes look at Authorization Access / Scopes.
  • scim-status: String; can be either “activated” or “waiting-activation” for orgs that are not activated yet. An org that is not activated won’t be able to do something useful except configure modules and see its own profile.
  • allow-all-role-to-login: Boolean; If true, non-admins can login to IROH1. Only Masters can edit this value at the top level of the object. If this value is not present we use the one in the settings block. If none is set, the decision to allow a non-admin of an org to login is the default of the Identity Provider (IdP) of the user. The IdP can be either AMP or Threatgrid. By default AMP non-admins are not allowed to login. By default Threatgrid allows non-admins to login.
  • enabled?: Boolean; Only masters can change this value. When disabled no users of this org can login.
  • settings: OrgSettings; A user editable settings object. To edit these settings you’ll need to be an Org admin.

The settings field contains an OrgSettings object whose only potential field is:

  • allow-all-role-to-login : Boolean ; If the top-level similar flag is not set then:
    • If true, non-admin can login to IROH1.
    • If false only admin can login.
    • If not set use the default value of the Identity Provider (false for AMP, true for Threatgrid).

User Model

{"user-id":"idb-amp:some-user-identifier",
 "org-id":"some-unique-org-id",
 "scopes":["enrich" "private-intel" "public-intel"],
 "additional-scopes":["super" "sse"],
 "idp-mappings": [{"idp":"idb-amp",
                   "user-identity-id":"some-user-identifier",
                   "organization-id":"some-org-id" }],
 "user-name":"Jane Doe",
 "user-email":"dev.null@cisco.com",
 "user-nick":"Jannie"}

Description of all the fields.

  • user-id: String; (mandatory field) a unique identifier for the user across all IROH1
  • org-id: String; (mandatory field) a unique identifier for the org of the user across all IROH1
  • scopes: [String]; (mandatory field) a list of scopes
  • additional-scopes: [String]; list of additional scopes that can be set by Masters2.
  • idp-mappings: [IdPMapping] object; contains technical information about mapping between the identifiers in IROH1 and from the Identity Providers3. This mapping should only be useful for debugging purposes. It might disappear in the future and should certainly not be used directly.
  • user-name: String; if provided should be the full user name.
  • user-nick: String; if provided should be the user nickname, this should be the preferred field to display the user identity to the user. The user-id will not be human friendly.
  • user-email: String; if provided should be the user email.

Authorization Access / Scopes

The main authorization model is based on scopes. This is clearly driven by the OAuth2 specification. A scope is a simple string that contains no space which is interpreted by the API to accept, reject, modify the behavior of some call.

Here is an exhaustive list of safe root4 scopes:

scope description
admin Provide admin privileges
casebook Access and modify your casebooks
global-intel Access AMP Global Intelligence
private-intel Access Private Intelligence
enrich Query your configured modules for threat intelligence
feedback Submit Customer Feedback
inspect Extract Observables and data from text
integration Manage your modules
investigation Perform threat analysis investigation
invite Invite users into your organization
notification Receive notifications from integrations
oauth Manage OAuth2 Clients
profile Get your profile information
registry Manage registry entries.
response List and execute response actions using configured modules
users Manage users of your organisation
telemetry Collect application data for analytics
webhook Manage your Webhooks
event Read IROH Events
cognitive Cognitive Integration
orbital Orbital Integration
sse SSE Integration. Manage your Devices.

Most root scopes relates to a specific API endpoint. They all have a dedicated Swagger UI page:

scope URL
casebook https://intel.amp.cisco.com/#/Casebook
global-intel https://intel.amp.cisco.com
private-intel https://private.intel.amp.cisco.com
enrich https://visibility.amp.cisco.com/iroh/iroh-enrich
inspect https://visibility.amp.cisco.com/iroh/iroh-inspect
integration https://visibility.amp.cisco.com/iroh/iroh-enrich/#/Settings
investigation https://visibility.amp.cisco.com/iroh/investigation
invite https://visibility.amp.cisco.com/iroh/invite
notification https://visibility.amp.cisco.com/iroh/notification
oauth https://visibility.amp.cisco.com/iroh/oauth2-clients
profile https://visibility.amp.cisco.com/iroh/profile
registry https://visibility.amp.cisco.com/iroh/registry
response https://visibility.amp.cisco.com/iroh/iroh-response
users https://visibility.amp.cisco.com/iroh/user-mgmt
telemetry https://visibility.amp.cisco.com/iroh/telemetry
webhook https://visibility.amp.cisco.com/iroh/iroh-webhook
event
sse https://visibility.amp.cisco.com/iroh/iroh-sse

Sub Scopes

Read / Write

Scopes are just strings, but the IROH1 API supports a generic format of those scopes to provide fine-grained access. The first level is to provide read-only access to a scope. For that we use the suffix :read. So for example:

  • global-intel:read is generally the scope you have by default. It means you can only access read-only routes of AMP Global Intelligence. If you try to access a non read-only route, you’ll get an HTTP 403 Forbidden response status.

Sometime the scopes of your user cannot return a 403 but instead change the results. For example the Response API in IROH1 provides both response call and references. If you ask for all possible responses with a scope limited to response:read, you’ll only get the response you can trigger with a read-only user. And thus you’ll get only references. More concretely you’ll be able to have a link to the AMP dashboard about some hash, but you won’t be able to put/remove a hash from an AMP list.

Scopes Tree Structure

In the previous section we explained how appending :read to some scope limits the access to read-only routes. There is also a mechanism that provides only access to some sub-trees of some API. For that we use / to only grant access to a sub-set of all the scopes provided by the root scope.

We can replace the full scope enrich by the restricted one enrich/health:read to only grant access to the health routes to a client / user.

Application Integration

The first step before integrating with IROH1 is to determine what kind of integration you want to achieve. It depends on your needs, and on some technical details.

The first question is about Users. Do you have your own user DB? Do users login directly into your application?

If the answer is yes, then the best way to integrate with IROH1 is to use an OAuth2 mechanism. IROH1 supports multiple OAuth2 Grants. So you need to ask yourself if you are in one of the following cases:

You have your own users and authentication mechanism

OAuth2 Client Credentials (scripts)

OAuth2 supports multiple methods to securely access the API. One specific access method is called Client Credential Grants which is suited for scripts.

To give you an overview:

  • each user goes to IROH1 and creates a client (you should provide the correct information to your users so they can create a suitable client for you)
  • each user then enters the credentials into your application
  • the application uses those credentials to access IROH1 API on behalf of the user.
Create The Client
Via the UI

Next to your login name should be a cog shaped icon. When you click on it, you will be redirected to the client creation page.

Enter the following information:

  • a name
  • a list of scopes (what kind of accesses do you wish to grant your client)
  • a description

    Pay close attention when you fill the form. Important! Please enter the information carefully. You will not be able to edit the client in the future. If you need to change something in the future, you will need to create another client.

Once done you are presented with a credential pair: client id and client password.

Keep those securely. Please note that the client password can only be displayed once. After you close the modal there is NO WAY to recover the client password. You will need to create another client if the credentials are lost.

Via the Swagger UI Interface

Browse:

https://visibility.amp.cisco.com/iroh/oauth2-clients/

This will provide raw access to the API to create a client, however, the only way to work with this API is by using a JWT with the oauth scope.

If you are already logged in to https://visibility.amp.cisco.com, you will not need to enter any information in the Authorization header inputs in Swagger. We take care of copying your session credentials in Swagger for you.

You’ll then be able to create your own client there.

Try retrieving the list of your scopes. You will not be able to create a client with more scopes than you’re allowed to access.

To get that list of scopes simply browse here:

https://visibility.amp.cisco.com/iroh/profile/#!/Profile/get_iroh_profile_whoami

Call the /whoami route. It should answer with a JSON containing your user and org information including the list of your scopes. See Data Model for more detail about this information:

{
    "user": {
        "scopes": [
            "admin",
            "casebook",
            "cisco",
            "collect",
            "enrich",
            "global-intel:read",
            "inspect",
            "integration",
            "oauth",
            "private-intel",
            "profile",
            "response",
            "ui-settings"
            "users"
        ],
        "updated-at": "2019-04-30T13:54:21.641Z",
        "user-email": "dev.null@cisco.com",
        "org-id": "13375cf9-561c-4958-0000-6d84b7ef09d4",
        "user-id": "idb-amp:13375ee9-2e3a-4e1b-977d-961facb5fd84",
        "idp-mappings": [{
            "idp": "idb-amp",
            "user-identity-id": "13375ee9-2e3a-4e1b-977d-961facb5fd84",
            "organization-id": "13375cf9-561c-4958-0000-6d84b7ef09d4"
        }],
        "enabled?": true,
        "last-logged-at": [
            "2019-04-30T13:54:21.843Z"
        ],
        "created-at": "2019-04-30T13:54:21.622Z"
    },
    "org": {
        "scim-status": "activated",
        "name": "IROH Testing",
        "enabled?": true,
        "id": "13375cf9-561c-4958-0000-6d84b7ef09d4",
        "created-at": "2019-04-30T13:54:21.634Z"
    }
}
OAuth2 Client Model

Client mandatory fields:

  • id the unique id of the client across all IROH1,
  • name a name for the client that will be user facing,
  • client-type can be either confidential or public (no secret),
  • grants a list that could contain auth-code, client-creds or device-grant,
  • redirects a set of URIs (only for auth-code grant clients),
  • scopes a set of scopes,
  • enabled? a boolean, this field is editable by admin users,
  • approved? a boolean, editable only by IROH1 admins.

Client optional fields:

  • description, a long description of the client that could be presented to the users during client authorization,
  • allow-partial-user-scopes? If true, a user without all the requested scopes from the client can still authorize the client. The refresh and access tokens will only have the intersection of both scopes.
  • availability, can be user, org or everyone. This filters the user that can grant access to this client:
    • user only the owner
    • org only by members of the org
    • everyone any IROH1 user
  • audiences The list of audiences to add to the JWT related to this client. See https://tools.ietf.org/html/rfc7519#section-4.1.3 and https://openid.net/specs/openid-connect-core-1_0.html#IDToken
  • owner-id, the user id of the client’s owner,
  • org-id, the org id of the client’s owner,
  • password, also known as the “client’s secret”, public clients don’t have a password,
  • access-token-lifetime-in-sec, access token lifetime this client provides,
  • refresh-token-lifetime-in-sec, refresh token lifetime this client provides,
  • enabled-by, the user id of the admin that enabled the client,
  • disabled-by, the user id of the admin that disabled the client,
  • approval-status, possible values are waiting rejected approved. During client creation some criteria will need an IROH1 Admin’s approval,
  • approver-id, the user id of the user (a IROH1 admin) that approved the client
  • approval-message, a message left for the user.
  • client-preset-id, a Client Preset ID. More about Client Presets at Client Presets.
Client Presets

Client Presets are objects that represent a partial construction of an OAuth2 Client. Client Presets have global visibility. They are managed by SecureX Administrators, thus you cannot create a client preset.

A given Client can associate a client_preset_id. When doing so, the complete Client that consumers will perceive will be a merging of the Client fields and the Client Preset:

perceived client = <client> + <client preset>

This feature has the goal of making Clients more manageable. It will be easier for end users to create new client for known specific needs.

  • Override rules

    In the case that a same field is defined in both the Client and the Preset:

    • For scalar values, the Client value will take precedence
    • For collection values, the fields will be merged (typically for scopes and redirects).
Use the Client

If everything worked correctly then you should have in your possession a client_id and a client_password.

You can’t access the API directly with these credentials because OAuth2 requires the use of an intermediate call in order to make all the necessary security checks. This call will provide you with an access token. An access token is a token you can use to access the API during a limited period.

The IROH1 access tokens are JWT. Once you have a JWT, you can use it until it expires.

When the access token will expire you’ll need to ask for another one.

Get the Access Token

In order to retrieve the access token you need to make a simple request to https://visibility.amp.cisco.com/iroh/oauth2/token

So here is a real world example using bash with a curl command:

client_id="client-38bbc74d-f7d1-452b-8777-fb8c7d985477"
client_password="FIz1FDf40ms0mNpc9oS8AJQJ2Vyw0aUtE17XWZWEQ71IMs13J8AQFQ"
curl -X POST \
    -u "$client_id:$client_password" \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --header 'Accept: application/json' \
    -d 'grant_type=client_credentials' \
    'https://visibility.amp.cisco.com/iroh/oauth2/token'

And the response:

{"access_token":"eyJhbGciO..."
 ,"token_type":"bearer"
 ,"expires_in":600
 ,"scope":"enrich:read casebook inspect:read"}

So with this token, you’ll be able to access:

  • all casebook routes
  • enrich read only routes
  • inspect read only routes.

    The access token will expire in 600 seconds.

    Important Remarks

    We use Basic Auth, in order to manually supply the correct header don’t forget to base64 encode the client_id:client_password string. The two following lines are interchangeable:

    curl -u "client_id:client_password" https://example.com
    creds=$(echo -n client_id:client_password | base64)
    curl -H "Authorization: Basic $creds" https://example.com
    
Call the API using the Access Token

Now that you have retrieved the access token (which is a JWT). You can call the API with it, however you will be restricted to the scopes you granted to your client.

ACCESS_TOKEN="eyJhbGciO..."
curl -X POST \
    --header 'Content-Type: application/json' \
    --header 'Accept: application/json' \
    --header "Authorization: Bearer $ACCESS_TOKEN" \
    -d '{"content":"cisco.com"}' \
    'https://visibility.amp.cisco.com/iroh/iroh-inspect/inspect'
[{"value":"cisco.com","type":"domain"}]
APIs and their Scopes

IROH Provides multiple APIs each provides a swagger.json file and an integrated Swagger UI web page.

To see an exhaustive list of scopes and links to their swagger UI interfaces see Authorization Access / Scopes.

Checklist

Verify your integration satisfies the Client Checklist.

OAuth2 Authorization Code Grant Credentials (Web Applications)

The OAuth2 Authorization Code Grant is described in the RFC6749. This provides the ability for a user to grant access to your web application. Your application will be able to call the API on behalf of multiple users. This is both easier and more secure than the client credentials grant.

With this workflow the user will not need to copy/paste any credentials. He just needs to click on a button to authorize your client.

To give an overview of the process on how to integrate:

  1. Create a single client for your application.
  2. Generate a link on your web application. When the user clicks on it, he is redirected to IROH1, and he can grant access to your application. The user returns to your website with a secret code and you will then retrieve a refresh token that you should keep securely.
  3. With the refresh token you can retrieve an access token without user interaction.
  4. The access token can be used to call the IROH1 APIs on behalf of the user.

Notes:

  • Step 1 should be done only once.
  • Step 2 should be done only once per user.
  • Step 3 should be done only when you want to call the API and the access token has expired (every 10 min by default).
  • Step 4 should be done for all API calls, so it is highly recommended to cache the access tokens and not to call the refresh token for each API call to IROH1.
Create a Client

You can create an Authorization Code Grant client via the Admin/Clients section of SecureX or directly via the API.

Notes:

  • before creating the client, you must set its field grants to ["auth-code"] in order to support Authorization Code Grant workflow.
  • If you want to be autonomous, you’ll need to have control of an HTTPS URL (generally something along https://my.domain.tld/path/to/callback). This is the URL you’ll use to set in the redirects value of the client.

Depending on the information you’ll provide during client creation, the client won’t be automatically approved. The client will be approved during creation if all the following conditions are met:

For more details about each field refer to the section OAuth2 Client Model. To create a client refer to the section Via the Swagger UI Interface.

client for all IROH1 users

If you need to open your web application to all users and not only to the members of your organization you will need to create a client with the availability field equal to everyone. The client won’t be approved and you’ll need to reach out to a IROH1 administrator to approve it.

redirects to localhost without https

For client with availability set to everyone, every URI in the redirects array must be https. If you need to you use another scheme in your URI then you will need to reach out an administrator to approve your client.

public clients

If your application is a frontend only application, having a password does not make sense. For this specific case it is recommended (by the RFC) to not use the Implicit grant, instead use an Authorization Code Grant client without any password. In this case you must set the client-type to public.

This is also a very specific need. You will need to request approval from a IROH1 administrator.

Token lifetime

The current default lifetime of access token is 10 minutes. The current default lifetime for refresh token is unlimited.

If you want longer lived access token or shorter lived refresh token you simply need to set the fields access-token-lifetime-in-sec and refresh-token-lifetime-in-sec when creating your client. You will need to request a IROH1 administrator approve your client.

☞ After a refresh-token lifetime, the refresh token will not work anymore. After half the lifetime, when getting a new access token you will also get a newer refresh token with the same lifetime. It is then up to you, the client, to perform a new refresh token call during the second half or 1h before the expiration to get a new refresh token. After a new refresh-token is provided, the old one can be revoked anytime so you should cease using the old refresh token.

Without a valid refresh token you’ll be forced to reengage the user in an interactive authorization workflow.

Use the Client

☞ If you have not yet created a client that can be used for Authorization Code Grant, please refer to the section Create a Client.

If you have created a client and it is approved. You should have the following information:

  • client-id
  • client-password
  • redirect-uri; one of the URL you set in the redirects array of the client
  • scopes; the list of scope you’ll ask IROH1 users to grant to your app

You will also need to know the authorization and token URLs of IROH1. You can get them at the metadata endpoint (c.f. OAuth2 Metadata).

Present an URL to the User

The first step is to present the user with a URL (return line here for readability):

https://visibility.amp.cisco.com/iroh/oauth2/authorization?
   response_type=code
   & client_id=YOUR_CLIENT_ID
   & redirect_uri=YOUR_REDIRECT_URL
   & scope=LIST_OF_SCOPES_SPACE_SEPARATED
   & state=A_DIFFICULT_TO_GUESS_RANDOM_STRING

Once the user clicks on the link they will be redirected to IROH1 and asked to grant access to your web application. If the user refuses or accepts, the user will be redirected to the redirecturi with parameters that will contain further information.

Getting the Refresh Token

You should listen to your redirect_uri and parse the query parameters. An example of URL the user will be redirected to after granting your app the access would be:

YOUR_REDIRECT_URL?state=A_DIFFICULT_TO_GUESS_RANDOM_STRING&code=SOME_CODE

Then you must verify the state is the same one the user has clicked on. (In our example state must be A_DIFFICULT_TO_GUESS_RANDOM_STRING).

You can then get the refresh token using the code by making a POST request to .

curl -XPOST \
     -u "YOUR_CLIENT_ID:YOUR_CLIENT_PASSWORD" \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -d "grant_type=authorization_code&redirect_uri=YOUR_REDIRECT_URL&code=SOME_CODE" \
     https://visibility.amp.cisco.com/iroh/oauth2/token

The `-u` is to use Basic Auth, with client-id as user and client-password as password. It is also very important to use the content type application/x-www-form-urlencoded. Note that the route does not support parameters sent via a JSON content type. (cf. RFC6749 section 4.1.3)

The server should then return a successful response (cf. RFC6749 section 4.1.4):

  {"access_token":"eyJhbG...",
   "token_type":"bearer",
   "expires_in":600,
   "refresh_token":"eJJyFFA..."}

IMPORTANT: The POST to the /token route exposes your client password, so it must be done server side not inside the browser.

You should have both an access token and a refresh token. The access token will expire in 600 seconds. But the refresh token shouldn’t expire soon. Take care of saving the refresh token for your user. Each time you’ll need a new fresh access token you’ll need the refresh token.

Using the access token you can make calls to the IROH1 API on behalf of the user but will be restricted to the scope they granted your app.

An example of a call to the API would be to retrieve the user profile (for that your client should have the scope “profile” in the list of scopes).

> access_token="eyJhbG..."
> curl -XGET \
       -H "Accept: application/json" \
       -H "Authorization: Bearer $access_token" \
       https://visibility.amp.cisco.com/iroh/profile/whoami
Getting a new Access Token

After a few minutes, the access token will expire. API access after token expiration will require user interaction. You will only need to use the refresh token of the user to get a new access token.

The call to refresh a token uses the same endpoint you used to get it the first time. But instead of using a grant_type of authorization_code, you will use a grant_type equal to refresh_token (cf. RFC6749 section 6).

curl -XPOST \
     -u "YOUR_CLIENT_ID:YOUR_CLIENT_PASSWORD" \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -d "grant_type=refresh_token&refresh_token=REFRESH_TOKEN" \
     https://visibility.amp.cisco.com/iroh/oauth2/token

The server should then return a successful response:

  {"access_token":"eyJhbG...",
   "token_type":"bearer",
   "expires_in":600}

IMPORTANT: The response might also contain a new refresh token along with the new access token. In that case you must revoke the old refresh token and use the new one instead. IROH1 might accept the old refresh token for a short period to prevent bugs in distributed environments.

Tutorial

This section provides complete example. The credentials used will not be functional, but you could be able to follow all the steps.

Clone Demo project

Clone https://github.com/yogsototh/oauth2-client-demo

Serve it to your own domain, possibly in some directory. This tutorial assumes it will be: https://my.domain.tld/ctr-oauth/

If you cannot test directly from your domain and want to test on localhost check the Run on localhost section.

Create the client
POST https://visibility.amp.cisco.com/iroh/oauth2-clients/clients
Accept: application/json
Content-Type: application/json
User-Agent: ob-http
Authorization: Bearer eyJhbG...

{
  "scopes": [ "profile", "inspect" ],
  "description": "Developer Doc OAuth2 Test Client",
  "redirects": [ "https://my.domain.tld/ctr-oauth/callback.html" ],
  "availability": "org",
  "name": "OAuth2 Developer Doc Test",
  "grants": [ "auth-code" ],
  "client-type": "confidential"
}
{
  "scopes": [
    "profile",
    "inspect"
  ],
  "description": "Developer Doc OAuth2 Test Client",
  "approved?": true,
  "redirects": [
    "https://my.domain.tld/ctr-oauth/callback.html"
  ],
  "availability": "org",
  "password": "CrXwg31_vnRHpjPXzgVzUFKHr6RO8GTL-iI8aDeUU3n48NtD7PFLhg",
  "name": "OAuth2 Developer Doc Test",
  "org-id": "f47a89bf-5d2e-4392-b770-000000000000",
  "enabled?": true,
  "grants": [
    "auth-code"
  ],
  "client-type": "confidential",
  "id": "client-3bb1e787-381d-4f12-bf32-e1158f200ddc",
  "approval-status": "approved",
  "owner-id": "f0010924-e1bc-4b03-b600-000000000000",
  "created-at": "2019-07-25T14:15:29.117Z"
}
Configure the demo

Edit the info.js to look like:

var oauthURLPrefix="https://visibility.amp.cisco.com";
var oauthServerUrl=oauthURLPrefix + "/iroh/oauth2/authorize";
var oauthServerTokenUrl=oauthURLPrefix + "/iroh/oauth2/token";
var resourceProviderTestEndpoint=oauthURLPrefix + "/iroh/profile/whoami" ;
var response_type="code";
var client_id="client-3bb1e787-381d-4f12-bf32-e1158f200ddc";
var client_password = "CrXwg31_vnRHpjPXzgVzUFKHr6RO8GTL-iI8aDeUU3n48NtD7PFLhg";
var redirect_uri="https://my.domain.tld/ctr-oauth/callback.html";
var scopes=[ "profile", "inspect" ];
var scope=scopes.join(" ");
var state="whatever=";
Run the demo
  1. Visit https://my.domain.tld/ctr-oauth/index.html
  2. Click on the link, you will be redirected to IROH1
  3. Authorize the client
  4. Now back at https://my.domain.tld/ctr-oauth/callback.html you should inspect the call in your browser and play with the demo.
  5. Retrieve the access token and refresh token using the code.
  6. Get another access token using the refresh token.
Details about queries in the demo

While the workflow necessitates user interaction through the browser, we will describe most call in detail.

  • Code in callback URL

    The user interaction is only needed to authorize the client. He will then visit https://my.domain.tld/ctr-oauth/callback.html with the query parameters code and state.

    Here is a real example:

    https://my.domain.tld/ctr-oauth/callback.html?
        code=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL2VtYWlsIjoieWFlc3Bvc2lAY2lzY28uY29tIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9zY29wZXMiOlsiaXJvaC1hZG1pbiIsImludGVncmF0aW9uIiwicHJpdmF0ZS1pbnRlbCIsImFkbWluIiwicHJvZmlsZSIsImluc3BlY3QiLCJpcm9oLWF1dGgiLCJzc2UiLCJ1c2VycyIsImNpc2NvIiwiY2FzZWJvb2siLCJvcmJpdGFsIiwiZW5yaWNoIiwib2F1dGgiLCJnbG9iYWwtaW50ZWwiLCJjb2xsZWN0IiwicmVzcG9uc2UiLCJ1aS1zZXR0aW5ncyJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25pY2siOiJZYW5uIEVzcG9zaXRvIiwiZW1haWwiOiJ5YWVzcG9zaUBjaXNjby5jb20iLCJpc3MiOiJJUk9IIEF1dGgiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJleHAiOjE1NjQxMjk4NDcsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3VzZXJcL2lkIjoiZjAwMTA5MjQtZTFiYy00YjAzLWI2MDAtODljNmNmNTI3NTdjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vcmdcL2lkIjoiZjQ3YTg5YmYtNWQyZS00MzkyLWI3NzAtYWQ0ODIxYTgyYWNmIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwvZ3JhbnQiOiJhdXRoLWNvZGUiLCJqdGkiOiJmMjFiMDU5NS04YzhjLTRkZTktOGI4MS01NWVmZTIwMThlODMiLCJuYmYiOjE1NjQxMjkxODcsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25hbWUiOiJZYW5uIEVzcG9zaXRvIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9pZCI6ImYwMDEwOTI0LWUxYmMtNGIwMy1iNjAwLTg5YzZjZjUyNzU3YyIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL2NsaWVudFwvaWQiOiJjbGllbnQtM2JiMWU3ODctMzgxZC00ZjEyLWJmMzItZTExNThmMjAwZGRjIiwiaWF0IjoxNTY0MTI5MjQ3LCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL29hdXRoXC9raW5kIjoiY29kZS10b2tlbiJ9.IL5xGEfcwnKpd2KNMyo2_kNtkEsfwTa6vYKJHR6uF3L3CGX7edat_eYXNmLACgamX3pdYfXo8JAHEOvdr7zl6-fLq0SHlFdxy4pbVQbr8gJt6TAZVWgNu8wxJfTzL6-V0oIv6mcYD2NWTXYoA8R19u6iFnOtoTwXVx58Dk2UXQuueHmq2CejpJspGgjChAHsebh1XKl-6uPo7debCQvrzPhB56lGShKT0NgEqT_ZML032iFqGIBHUe6WdlqO_i6ji1-QX3CPmA_rwep7n0h1RuDxWib9YCtwJFh0SO0AQX_bK-0000000000000000000000000000000000000000
      & state=y5jaXNjby5jb21cL2lyb2hcL2lkZW
    

    The code represents an agreement from a IROH1 user to grant access to your application. Remember OAuth2 Authorization Code Grant is a kind of contract between 3 entities: your application (the client), the user, and IROH1. The code query parameter is a JWT. Once decoded it contains:

    {
      "https://schemas.cisco.com/iroh/identity/claims/user/email":
        "dev.null@cisco.com",
      "https://schemas.cisco.com/iroh/identity/claims/user/scopes": [
        "integration",
        "private-intel",
        "admin",
        "profile",
        "inspect",
        "sse",
        "users",
        "casebook",
        "enrich",
        "oauth",
        "global-intel:read",
        "collect",
        "response",
        "ui-settings"
      ],
      "https://schemas.cisco.com/iroh/identity/claims/user/nick":
        "Yann Esposito",
      "email": "dev.null@cisco.com",
      "iss": "IROH Auth",
      "https://schemas.cisco.com/iroh/identity/claims/scopes": [
        "profile",
        "inspect"
      ],
      "exp": 1564129847,
      "https://schemas.cisco.com/iroh/identity/claims/oauth/user/id":
        "f0010924-e1bc-4b03-b600-000000000000",
      "https://schemas.cisco.com/iroh/identity/claims/org/id":
        "f47a89bf-5d2e-4392-b770-000000000000",
      "https://schemas.cisco.com/iroh/identity/claims/oauth/grant":
        "auth-code",
      "jti": "f21b0595-8c8c-4de9-8b81-55efe2018e83",
      "nbf": 1564129187,
      "https://schemas.cisco.com/iroh/identity/claims/oauth/scopes": [
        "profile",
        "inspect"
      ],
      "https://schemas.cisco.com/iroh/identity/claims/user/name":
        "Yann Esposito",
      "https://schemas.cisco.com/iroh/identity/claims/user/id":
        "f0010924-e1bc-4b03-b600-000000000000",
      "https://schemas.cisco.com/iroh/identity/claims/oauth/client/id":
        "client-3bb1e787-381d-4f12-bf32-000000000000",
      "iat": 1564129247,
      "https://schemas.cisco.com/iroh/identity/claims/oauth/kind":
        "code-token"
    }
    

    The JWT claims are detailed in the Call the API / JWT section. Note that the JWT contains 3 different claims related to scopes:

    Another field to note is the oauth/kind that contains code-token. So this JWT can only be used as a temporary code during OAuth2 workflow.

    Notice also the expiration date (exp) is just 10 minutes after the creation date (iat).

    IMPORTANT Your client must check that the state parameter is the same as the state parameter in the link the user clicked on. Of course each time you present a link to grant your app to a user, you should generate a new strong random string.

  • Get the access and refresh token with the code

    With this code token (you don’t need to decode it) you can get a refresh token and an access token.

    POST https://visibility.amp.cisco.com/iroh/oauth2/token
    Accept: application/json
    Content-Type: application/x-www-form-urlencoded; charset=UTF-8
    User-Agent: ob-http
    Authorization: Basic Y2xpZW50LTNiYjFlNzg3LTM4MWQtNGYxMi1iZjMyLWUxMTU4ZjIwMGRkYzpDclh3ZzMxX3ZuUkhwalBYemdWelVGS0hyNlJPOEdUTC1pSThhRGVVVTNuNDhOdEQ3UEZMaGc=
    
    code=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL2VtYWlsIjoieWFlc3Bvc2lAY2lzY28uY29tIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9zY29wZXMiOlsiaXJvaC1hZG1pbiIsImludGVncmF0aW9uIiwicHJpdmF0ZS1pbnRlbCIsImFkbWluIiwicHJvZmlsZSIsImluc3BlY3QiLCJpcm9oLWF1dGgiLCJzc2UiLCJ1c2VycyIsImNpc2NvIiwiY2FzZWJvb2siLCJvcmJpdGFsIiwiZW5yaWNoIiwib2F1dGgiLCJnbG9iYWwtaW50ZWwiLCJjb2xsZWN0IiwicmVzcG9uc2UiLCJ1aS1zZXR0aW5ncyJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25pY2siOiJZYW5uIEVzcG9zaXRvIiwiZW1haWwiOiJ5YWVzcG9zaUBjaXNjby5jb20iLCJpc3MiOiJJUk9IIEF1dGgiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJleHAiOjE1NjQxMzA5NTAsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3VzZXJcL2lkIjoiZjAwMTA5MjQtZTFiYy00YjAzLWI2MDAtODljNmNmNTI3NTdjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vcmdcL2lkIjoiZjQ3YTg5YmYtNWQyZS00MzkyLWI3NzAtYWQ0ODIxYTgyYWNmIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwvZ3JhbnQiOiJhdXRoLWNvZGUiLCJqdGkiOiI2NWU4MGE0Yi03YTM2LTQzNDEtOGI0NC0zZTM2MTk4YjRiMTQiLCJuYmYiOjE1NjQxMzAyOTAsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25hbWUiOiJZYW5uIEVzcG9zaXRvIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9pZCI6ImYwMDEwOTI0LWUxYmMtNGIwMy1iNjAwLTg5YzZjZjUyNzU3YyIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL2NsaWVudFwvaWQiOiJjbGllbnQtM2JiMWU3ODctMzgxZC00ZjEyLWJmMzItZTExNThmMjAwZGRjIiwiaWF0IjoxNTY0MTMwMzUwLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL29hdXRoXC9raW5kIjoiY29kZS10b2tlbiJ9.yrOZloNpYGrs0eIWk9dVl-OoSgnSggTcCGrS-iPyhsefZ48SFeoWYtiktki8-uRi6hCjYwQ7WMOQ-MQ9Fr7w4ELMNZJeBSHZjHFty-wH_Fmx62NkVzdAty9pALD2sgqBMS44NWPlyzni1h7sP7wRRxr70o4XdPZUD-wIyjmM0Ewtf0tB-MEPfdXm51na-xt3RAd_hoCP3CubuSPYCDp9dQ-9AOzAAJfogRGboI_u1I38oMY_x_GRZUdyLdOHvVz4XaoBUfRgwS-C7eVu73ULavfJmd5W4UaO_LNV9oNgxQQD9B9JlLs7dMVzFxe6UaYi8KXUdk-qZRD5Px_lth7uVw
    &redirect_uri=https://my.domain.tld/ctr-oauth/callback.html
    &scope=profile%33inspect
    &grant_type=authorization_code
    
    {
      "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL2VtYWlsIjoieWFlc3Bvc2lAY2lzY28uY29tIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9zY29wZXMiOlsiaXJvaC1hZG1pbiIsImludGVncmF0aW9uIiwicHJpdmF0ZS1pbnRlbCIsImFkbWluIiwicHJvZmlsZSIsImluc3BlY3QiLCJpcm9oLWF1dGgiLCJzc2UiLCJ1c2VycyIsImNpc2NvIiwiY2FzZWJvb2siLCJvcmJpdGFsIiwiZW5yaWNoIiwib2F1dGgiLCJnbG9iYWwtaW50ZWwiLCJjb2xsZWN0IiwicmVzcG9uc2UiLCJ1aS1zZXR0aW5ncyJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25pY2siOiJZYW5uIEVzcG9zaXRvIiwiZW1haWwiOiJ5YWVzcG9zaUBjaXNjby5jb20iLCJzdWIiOiJmMDAxMDkyNC1lMWJjLTRiMDMtYjYwMC04OWM2Y2Y1Mjc1N2MiLCJpc3MiOiJJUk9IIEF1dGgiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJleHAiOjE1NjQxMzA5NzMsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3VzZXJcL2lkIjoiZjAwMTA5MjQtZTFiYy00YjAzLWI2MDAtODljNmNmNTI3NTdjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vcmdcL2lkIjoiZjQ3YTg5YmYtNWQyZS00MzkyLWI3NzAtYWQ0ODIxYTgyYWNmIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwvZ3JhbnQiOiJhdXRoLWNvZGUiLCJqdGkiOiI1Y2Y1NGRkMi0zMmNlLTRkOTctODEzYy0wZWFiYzZlZWI1MjEiLCJuYmYiOjE1NjQxMzAzMTMsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25hbWUiOiJZYW5uIEVzcG9zaXRvIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9pZCI6ImYwMDEwOTI0LWUxYmMtNGIwMy1iNjAwLTg5YzZjZjUyNzU3YyIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL2NsaWVudFwvaWQiOiJjbGllbnQtM2JiMWU3ODctMzgxZC00ZjEyLWJmMzItZTExNThmMjAwZGRjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC92ZXJzaW9uIjoidjEuMjAuMC1kZjgxNGU3YjYyODMwZGRkNTc2ZCIsImlhdCI6MTU2NDEzMDM3MywiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwva2luZCI6ImFjY2Vzcy10b2tlbiJ9.tZ75vCYYst9gSbnZl7LZ3GiVXRpAmOFy9cmUs9wGkSw7XSdRN-4sqwUkcCoNq161piZedf17PrGRMurEtq9x8cW1_YuU_5pi9mDMH1ayQYNXJXkk6G_PJTbzyBLosdp3IAc_GN7DrCZoHx9-oveCy0MfXTbgrjtsx7BoELynECbjTPAfi8CU_rPRCnIyrxLTFMJdqLHsUVP_MDyXEvV43EPw1uRUn9ZBf-dBAswdFsEhH8RF4IFtZyDxb49HTpoWHA54jFWiYAP3cISyS6IKI8H1aVQHAUDbdSzunx0Rp5U9xJysZiAZzVkiSUGTJqXQt_00000000000000000000",
      "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL2VtYWlsIjoieWFlc3Bvc2lAY2lzY28uY29tIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9zY29wZXMiOlsiaXJvaC1hZG1pbiIsImludGVncmF0aW9uIiwicHJpdmF0ZS1pbnRlbCIsImFkbWluIiwicHJvZmlsZSIsImluc3BlY3QiLCJpcm9oLWF1dGgiLCJzc2UiLCJ1c2VycyIsImNpc2NvIiwiY2FzZWJvb2siLCJvcmJpdGFsIiwiZW5yaWNoIiwib2F1dGgiLCJnbG9iYWwtaW50ZWwiLCJjb2xsZWN0IiwicmVzcG9uc2UiLCJ1aS1zZXR0aW5ncyJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25pY2siOiJZYW5uIEVzcG9zaXRvIiwiZW1haWwiOiJ5YWVzcG9zaUBjaXNjby5jb20iLCJpc3MiOiJJUk9IIEF1dGgiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJleHAiOjE3NTE0MTQ0MDAwLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL29hdXRoXC91c2VyXC9pZCI6ImYwMDEwOTI0LWUxYmMtNGIwMy1iNjAwLTg5YzZjZjUyNzU3YyIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb3JnXC9pZCI6ImY0N2E4OWJmLTVkMmUtNDM5Mi1iNzcwLWFkNDgyMWE4MmFjZiIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL2dyYW50IjoiYXV0aC1jb2RlIiwianRpIjoiMjc0YjkwYTAtZDlhNi00YmI2LWJiN2UtMDkzYTY4MzllMTUyIiwibmJmIjoxNTY0MTMwMzEzLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL29hdXRoXC9zY29wZXMiOlsicHJvZmlsZSIsImluc3BlY3QiXSwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9uYW1lIjoiWWFubiBFc3Bvc2l0byIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvdXNlclwvaWQiOiJmMDAxMDkyNC1lMWJjLTRiMDMtYjYwMC04OWM2Y2Y1Mjc1N2MiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL29hdXRoXC9jbGllbnRcL2lkIjoiY2xpZW50LTNiYjFlNzg3LTM4MWQtNGYxMi1iZjMyLWUxMTU4ZjIwMGRkYyIsImlhdCI6MTU2NDEzMDM3MywiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwva2luZCI6InJlZnJlc2gtdG9rZW4ifQ.abtBzZmX64XDTB7OJkcyCsOrGnXj-c6tSg89qg9ENnHSeBs-e4AhQMzHO5ZF5Cb4e7c8z34k0gxatuBqLTtLTztV32ktJGZ1IwFA4k7bpPrW1qdrSJQfZjOBBdLG3DSDMqo7dihqh_4VkfZGHDUNfao12xHTXCbzzfKiZNr6f8UJ0lzEvNZ2tQFkIMjdLVrN7OSk5K56-4SwEL6-X7LHRjF8M0FrGq9QU2lztuRyGXfbPOIal4wJdcfZ-Z2S1_fC82y-rE2mEDEX-diCI_oC_01wFiBbklJLoUEYK6Ry0000000000000000000000000000000000000000000000",
      "token_type": "bearer",
      "expires_in": 600,
      "scope": "profile inspect"
    }
    

    Notes:

    • The Authorization header is for Basic Auth, so it contains Basic followed by a base64 encoded CLIENT-ID:CLIENT-PASSWORD.
    • The body is not JSON. The Content-Type is application/x-www-form-urlencoded.
    • The access token will expires in 600 seconds.
    • Both the access_token and refresh_token are JWT.
  • Refresh Token

    The refresh token is a JWT that once decoded contains:

    {
      "https://schemas.cisco.com/iroh/identity/claims/user/email":
        "dev.null@cisco.com",
      "https://schemas.cisco.com/iroh/identity/claims/user/nick":
        "Yann Esposito",
      "email": "dev.null@cisco.com",
      "iss": "IROH Auth",
      "https://schemas.cisco.com/iroh/identity/claims/scopes": [
        "profile",
        "inspect"
      ],
      "exp": 17514144000,
      "https://schemas.cisco.com/iroh/identity/claims/oauth/user/id":
        "f0010924-e1bc-4b03-b600-000000000000",
      "https://schemas.cisco.com/iroh/identity/claims/org/id":
        "f47a89bf-5d2e-4392-b770-000000000000",
      "https://schemas.cisco.com/iroh/identity/claims/oauth/grant":
        "auth-code",
      "jti": "64aae4a3-b99b-4215-8f63-c8bf46a4d4b5",
      "nbf": 1564130650,
      "https://schemas.cisco.com/iroh/identity/claims/user/name":
        "Yann Esposito",
      "https://schemas.cisco.com/iroh/identity/claims/user/id":
        "f0010924-e1bc-4b03-b600-000000000000",
      "https://schemas.cisco.com/iroh/identity/claims/oauth/client/id":
        "client-3bb1e787-381d-4f12-00000000000000000",
      "iat": 1564130710,
      "https://schemas.cisco.com/iroh/identity/claims/oauth/kind":
         "refresh-token"
    }
    

    Your application must save this refresh token for this user.

  • Access Token

    The access token is also a JWT that you can use to call the API. In our example the client asked for the profile scope that grants access to the route /iroh/profile/whoami.

    GET https://visibility.amp.cisco.com/iroh/profile/whoami
    Accept: application/json
    Content-Type: application/json; charset=UTF-8
    User-Agent: ob-http
    Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL2VtYWlsIjoieWFlc3Bvc2lAY2lzY28uY29tIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9zY29wZXMiOlsiaXJvaC1hZG1pbiIsImludGVncmF0aW9uIiwicHJpdmF0ZS1pbnRlbCIsImFkbWluIiwicHJvZmlsZSIsImluc3BlY3QiLCJpcm9oLWF1dGgiLCJzc2UiLCJ1c2VycyIsImNpc2NvIiwiY2FzZWJvb2siLCJvcmJpdGFsIiwiZW5yaWNoIiwib2F1dGgiLCJnbG9iYWwtaW50ZWwiLCJjb2xsZWN0IiwicmVzcG9uc2UiLCJ1aS1zZXR0aW5ncyJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25pY2siOiJZYW5uIEVzcG9zaXRvIiwiZW1haWwiOiJ5YWVzcG9zaUBjaXNjby5jb20iLCJzdWIiOiJmMDAxMDkyNC1lMWJjLTRiMDMtYjYwMC04OWM2Y2Y1Mjc1N2MiLCJpc3MiOiJJUk9IIEF1dGgiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJleHAiOjE1NjQxMzA5NzMsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3VzZXJcL2lkIjoiZjAwMTA5MjQtZTFiYy00YjAzLWI2MDAtODljNmNmNTI3NTdjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vcmdcL2lkIjoiZjQ3YTg5YmYtNWQyZS00MzkyLWI3NzAtYWQ0ODIxYTgyYWNmIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwvZ3JhbnQiOiJhdXRoLWNvZGUiLCJqdGkiOiI1Y2Y1NGRkMi0zMmNlLTRkOTctODEzYy0wZWFiYzZlZWI1MjEiLCJuYmYiOjE1NjQxMzAzMTMsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25hbWUiOiJZYW5uIEVzcG9zaXRvIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9pZCI6ImYwMDEwOTI0LWUxYmMtNGIwMy1iNjAwLTg5YzZjZjUyNzU3YyIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL2NsaWVudFwvaWQiOiJjbGllbnQtM2JiMWU3ODctMzgxZC00ZjEyLWJmMzItZTExNThmMjAwZGRjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC92ZXJzaW9uIjoidjEuMjAuMC1kZjgxNGU3YjYyODMwZGRkNTc2ZCIsImlhdCI6MTU2NDEzMDM3MywiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwva2luZCI6ImFjY2Vzcy10b2tlbiJ9.tZ75vCYYst9gSbnZl7LZ3GiVXRpAmOFy9cmUs9wGkSw7XSdRN-4sqwUkcCoNq161piZedf17PrGRMurEtq9x8cW1_YuU_5pi9mDMH1ayQYNXJXkk6G_PJTbzyBLosdp3IAc_GN7DrCZoHx9-oveCy0MfXTbgrjtsx7BoELynECbjTPAfi8CU_rPRCnIyrxLTFMJdqLHsUVP_MDyXEvV43EPw1uRUn9ZBf-dBAswdFsEhH8RF4IFtZyDxb49HTpoWHA54jFWiYAP3cISyS6IKI8H1aVQHAUDbdSzunx0Rp5U9xJysZiAZzVkiSUGTJqXQt_00000000000000000000
    
    {
      "user": {
        "scopes": [
          "integration",
          "private-intel",
          "admin",
          "profile",
          "inspect",
          "iroh-auth",
          "sse",
          "users",
          "cisco",
          "casebook",
          "enrich",
          "oauth",
          "global-intel:read",
          "collect",
          "response",
          "ui-settings"
        ],
        "updated-at": "2019-07-25T14:53:21.721Z",
        "user-email": "dev.null@cisco.com",
        "user-name": "Yann Esposito",
        "org-id": "f47a89bf-5d2e-4392-b770-000000000000",
        "user-id": "f0010924-e1bc-4b03-b600-000000000000",
        "idp-mappings": [{
          "idp": "idb-amp",
          "user-identity-id": "f0010924-e1bc-4b03-b600-000000000000",
          "organization-id": "f47a89bf-5d2e-4392-b770-000000000000"
        }],
        "enabled?": true,
        "last-logged-at": [
          "2019-07-25T14:53:22.183Z",
          "2019-07-25T13:05:50.488Z",
          "2019-07-19T14:37:09.021Z",
          "2019-07-18T12:11:51.297Z",
          "2019-07-12T14:58:36.694Z"
        ],
        "created-at": "2018-09-21T14:52:38.079Z",
        "user-nick": "Yann Esposito"
      },
      "org": {
        "scim-status": "activated",
        "name": "Cisco - IROH Team",
        "updated-at": "2019-07-22T15:27:10.845Z",
        "enabled?": true,
        "additional-scopes": [
          "integration",
          "admin",
          "sse",
        ],
        "settings": {
          "allow-all-role-to-login": false
        },
        "id": "f47a89bf-5d2e-4392-b770-000000000000",
        "created-at": "2018-09-06T04:44:12.512Z"
      }
    }
    
  • Get a new access token with the refresh token

    After a few minutes (10 by default), the access token will expire. You’ll then need to get a new access token. You shouldn’t ask the user to authorize your application again in IROH1.

    You can get a new access token just by using the refresh token of that user and your client credentials.

    POST https://visibility.amp.cisco.com/iroh/oauth2/token
    Accept: application/json
    Content-Type: application/x-www-form-urlencoded; charset=UTF-8
    User-Agent: ob-http
    Authorization: Basic Y2xpZW50LTNiYjFlNzg3LTM4MWQtNGYxMi1iZjMyLWUxMTU4ZjIwMGRkYzpDclh3ZzMxX3ZuUkhwalBYemdWelVGS0hyNlJPOEdUTC1pSThhRGVVVTNuNDhOdEQ3UEZMaGc=
    
    refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL2VtYWlsIjoieWFlc3Bvc2lAY2lzY28uY29tIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9zY29wZXMiOlsiaXJvaC1hZG1pbiIsImludGVncmF0aW9uIiwicHJpdmF0ZS1pbnRlbCIsImFkbWluIiwicHJvZmlsZSIsImluc3BlY3QiLCJpcm9oLWF1dGgiLCJzc2UiLCJ1c2VycyIsImNpc2NvIiwiY2FzZWJvb2siLCJvcmJpdGFsIiwiZW5yaWNoIiwib2F1dGgiLCJnbG9iYWwtaW50ZWwiLCJjb2xsZWN0IiwicmVzcG9uc2UiLCJ1aS1zZXR0aW5ncyJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25pY2siOiJZYW5uIEVzcG9zaXRvIiwiZW1haWwiOiJ5YWVzcG9zaUBjaXNjby5jb20iLCJpc3MiOiJJUk9IIEF1dGgiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJleHAiOjE3NTE0MTQ0MDAwLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL29hdXRoXC91c2VyXC9pZCI6ImYwMDEwOTI0LWUxYmMtNGIwMy1iNjAwLTg5YzZjZjUyNzU3YyIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb3JnXC9pZCI6ImY0N2E4OWJmLTVkMmUtNDM5Mi1iNzcwLWFkNDgyMWE4MmFjZiIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL2dyYW50IjoiYXV0aC1jb2RlIiwianRpIjoiMjc0YjkwYTAtZDlhNi00YmI2LWJiN2UtMDkzYTY4MzllMTUyIiwibmJmIjoxNTY0MTMwMzEzLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL29hdXRoXC9zY29wZXMiOlsicHJvZmlsZSIsImluc3BlY3QiXSwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9uYW1lIjoiWWFubiBFc3Bvc2l0byIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvdXNlclwvaWQiOiJmMDAxMDkyNC1lMWJjLTRiMDMtYjYwMC04OWM2Y2Y1Mjc1N2MiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL29hdXRoXC9jbGllbnRcL2lkIjoiY2xpZW50LTNiYjFlNzg3LTM4MWQtNGYxMi1iZjMyLWUxMTU4ZjIwMGRkYyIsImlhdCI6MTU2NDEzMDM3MywiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwva2luZCI6InJlZnJlc2gtdG9rZW4ifQ.abtBzZmX64XDTB7OJkcyCsOrGnXj-c6tSg89qg9ENnHSeBs-e4AhQMzHO5ZF5Cb4e7c8z34k0gxatuBqLTtLTztV32ktJGZ1IwFA4k7bpPrW1qdrSJQfZjOBBdLG3DSDMqo7dihqh_4VkfZGHDUNfao12xHTXCbzzfKiZNr6f8UJ0lzEvNZ2tQFkIMjdLVrN7OSk5K56-4SwEL6-X7LHRjF8M0FrGq9QU2lztuRyGXfbPOIal4wJdcfZ-Z2S1_fC82y-rE2mEDEX-diCI_oC_01wFiBbklJLoUEYK6RylEQ9lIMgYsUNsw07nM3IWua2cq5O6rNWEe9yJJzo5TIuQg
    &scope=profile%33inspect
    &grant_type=refresh_token
    
    {
      "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL2VtYWlsIjoieWFlc3Bvc2lAY2lzY28uY29tIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9zY29wZXMiOlsiaXJvaC1hZG1pbiIsImludGVncmF0aW9uIiwicHJpdmF0ZS1pbnRlbCIsImFkbWluIiwicHJvZmlsZSIsImluc3BlY3QiLCJpcm9oLWF1dGgiLCJzc2UiLCJ1c2VycyIsImNpc2NvIiwiY2FzZWJvb2siLCJvcmJpdGFsIiwiZW5yaWNoIiwib2F1dGgiLCJnbG9iYWwtaW50ZWwiLCJjb2xsZWN0IiwicmVzcG9uc2UiLCJ1aS1zZXR0aW5ncyJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25pY2siOiJZYW5uIEVzcG9zaXRvIiwiZW1haWwiOiJ5YWVzcG9zaUBjaXNjby5jb20iLCJzdWIiOiJmMDAxMDkyNC1lMWJjLTRiMDMtYjYwMC04OWM2Y2Y1Mjc1N2MiLCJpc3MiOiJJUk9IIEF1dGgiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJleHAiOjE1NjQxMzQxODksImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3VzZXJcL2lkIjoiZjAwMTA5MjQtZTFiYy00YjAzLWI2MDAtODljNmNmNTI3NTdjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vcmdcL2lkIjoiZjQ3YTg5YmYtNWQyZS00MzkyLWI3NzAtYWQ0ODIxYTgyYWNmIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwvZ3JhbnQiOiJhdXRoLWNvZGUiLCJqdGkiOiIzNmY4YzgxNi1jN2E1LTQ0ZWMtYWZhNS1iMDI4OGIzMzJkZDkiLCJuYmYiOjE1NjQxMzM1MjksImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25hbWUiOiJZYW5uIEVzcG9zaXRvIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9pZCI6ImYwMDEwOTI0LWUxYmMtNGIwMy1iNjAwLTg5YzZjZjUyNzU3YyIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL2NsaWVudFwvaWQiOiJjbGllbnQtM2JiMWU3ODctMzgxZC00ZjEyLWJmMzItZTExNThmMjAwZGRjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC92ZXJzaW9uIjoidjEuMjAuMC1kZjgxNGU3YjYyODMwZGRkNTc2ZCIsImlhdCI6MTU2NDEzMzU4OSwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwva2luZCI6ImFjY2Vzcy10b2tlbiJ9.t8f5cwIBYMYlKSewpCAv2Sgh3xOhQS_MkQqFd14Xz2j9eZD_SpfLRSdYCt5glW9NMiMuUHqtzkYroK46zYeFpy5XoEx3gLZLJC4eJaVdIxRqRxt2IcJ5F7R31Xt_R5Cbkrgncl6NxynTeRyb--4UoQXlTVNmwXQxEGbM1bc9EU7iFabevJVswpj_no9Ah5zp2-BRtvszPzuF9-Ii5AUwmsAoVziH--uezFQQv5xhQNBA17fXjIn5D3oBiK50_vZEaAgdHE3SLDDPqw4BiPzeNipvE6oGuA486Bo3XET_O7DkgzzsEakwZe2HF2DKAstBLJSeMdRjA-fhBrRCLxHJSQ",
      "token_type": "bearer",
      "expires_in": 600,
      "scope": "profile inspect"
    }
    

    This is a very similar query to the one we used for code. The only differences are:

    • use refresh_token instead of code
    • use refresh_token for the grant_type parameter

    You must use Basic Auth with your client id and password, and use the application/x-www-form-urlencoded Content Type.

Run on localhost

For security, IROH1 does not allow clients with non https redirect URIs to be approved automatically.

In order to serve HTTPS locally you should follow the instructions to create a new self signed certificate.

git clone https://github.com/yogsototh/oauth2-client-demo
cd cert
./gen-new-root-cert.sh
./gen-new-cert.sh rootCA
cd ..

You should serve the site/ directory using the created server.crt and server.key.

A possible tool is sws. The easiest way to install it is to first install stack and then execute stack install sws.

You may also need to modify your browser to trust the self-signed certificate.

Checklist

⚠ Verify your integration follow the Client Checklist.

OAuth2 Device Grant

From the RFC 8628:

The OAuth 2.0 device authorization grant is designed for Internet- connected devices that either lack a browser to perform a user-agent- based authorization or are input constrained to the extent that requiring the user to input text in order to authenticate during the authorization flow is impractical. It enables OAuth clients on such devices (like smart TVs, media consoles, digital picture frames, and printers) to obtain user authorization to access protected resources by using a user agent on a separate device.

From a user standpoint
  1. Use your application on some device.
  2. The device show a short code and ask you to go to some URL (provide a link, perhaps with a QR Code)
  3. Go to IROH1 and verify the code is the same
  4. Authorize your application
  5. Close the browser and get back to the device.
  6. The device show a message, your account is connected.
From a developer standpoint
  1. Create and activate a IROH1 account
  2. Create the device grant client for your application

Your device should be able to use the client credentials. For every user:

  1. Make a call to IROH1 with the client credentials to retrieve:
    • user_code
    • device_code
    • verification_uri_complete to send the user to
    • verification_uri to send the user without pre-filling the user code
  2. Show the user code to the user and find a way to send the user to either verification_uri or verification_uri_complete.
  3. Concurrently, poll the IROH1 /token endpoint with the client credentials and the device_code. Make a call every few seconds (no more than once per second).
    • You will receive an “authorization pending” response if the user has not yet completed the authorization
    • If the user deny your application you will get the appropriate error message
    • If the user authorize your application you will receive both an access and a refresh token.
  4. If 3 succeed, then save the refresh token to get new access token to call IROH1 on behalf of the user.
Complete workflow example
Create The Client
  • Via the UI

    Device Grant Client creation is not yet supported by either SecureX nor Threat Response.

  • Via the Swagger UI Interface

    Browse:

    https://visibility.amp.cisco.com/iroh/oauth2-clients/

    This will provide raw access to the API to create a client, however, the only way to work with this API is by using a JWT with the oauth scope.

    If you are already logged in to https://visibility.amp.cisco.com, you will not need to enter any information in the Authorization header inputs in Swagger. We take care of copying your session credentials in Swagger for you.

    You’ll then be able to create your own client there.

    Try retrieving the list of your scopes. You will not be able to create a client with more scopes than you’re allowed to access.

    To get that list of scopes simply browse here:

    https://visibility.amp.cisco.com/iroh/profile/#!/Profile/get_iroh_profile_whoami

    Call the /whoami route. It should answer with a JSON containing your user and org information including the list of your scopes. See Data Model for more detail about this information:

    {
        "user": {
            "scopes": [
                "admin",
                "casebook",
                "cisco",
                "collect",
                "enrich",
                "global-intel:read",
                "inspect",
                "integration",
                "oauth",
                "private-intel",
                "profile",
                "response",
                "ui-settings"
                "users"
            ],
            "updated-at": "2019-04-30T13:54:21.641Z",
            "user-email": "dev.null@cisco.com",
            "org-id": "13375cf9-561c-4958-0000-6d84b7ef09d4",
            "user-id": "idb-amp:13375ee9-2e3a-4e1b-977d-961facb5fd84",
            "idp-mappings": [{
                "idp": "idb-amp",
                "user-identity-id": "13375ee9-2e3a-4e1b-977d-961facb5fd84",
                "organization-id": "13375cf9-561c-4958-0000-6d84b7ef09d4"
            }],
            "enabled?": true,
            "last-logged-at": [
                "2019-04-30T13:54:21.843Z"
            ],
            "created-at": "2019-04-30T13:54:21.622Z"
        },
        "org": {
            "scim-status": "activated",
            "name": "IROH Testing",
            "enabled?": true,
            "id": "13375cf9-561c-4958-0000-6d84b7ef09d4",
            "created-at": "2019-04-30T13:54:21.634Z"
        }
    }
    

    Once you are confident the set of scopes for your client is correct you can create a new device grant client like so:

    POST https://visibility.amp.cisco.com/iroh/oauth2-clients/clients
    Accept: application/json
    Content-Type: application/json
    User-Agent: ob-http
    Authorization: Bearer ${jwt}
    
    {
      "scopes": [ "profile", "inspect" ],
      "description": "Developer Doc OAuth2 Device Test Client",
      "name": "OAuth2 Developer Doc Test",
      "grants": [ "device-grant" ]
    }
    
    {
      "scopes": [
        "profile",
        "inspect"
      ],
      "description": "Developer Doc OAuth2 Device Test Client",
      "approved?": true,
      "redirects": [],
      "availability": "org",
      "password": "WJj1uXCzB8TioC4HA8ISIQFTuwYtJK_4tqDR4JHZ92E-hTa0uMu-Gg",
      "name": "OAuth2 Developer Doc Test",
      "org-id": "org-1",
      "enabled?": true,
      "grants": [
        "device-grant"
      ],
      "client-type": "confidential",
      "id": "client-5e7f8e3f-50e2-4139-86da-9e50bafca75a",
      "approval-status": "approved",
      "owner-id": "org-1-master-1",
      "created-at": "2021-07-21T13:54:04.989Z"
    }
    

    Take care the client password will be shown only once and could not be recovered by anyone. So keep this password safe.

    For more details about the client model see: OAuth2 Client Model

  • Use the client in the Device

    Once the user uses your device and want to connect its account to your application make a call to the device_authorization endpoint like so:

    POST https://visibility.amp.cisco.com/iroh/oauth2/device_authorization
    Accept: application/json
    Content-Type: application/x-www-form-urlencoded
    User-Agent: ob-http
    
    client_id=client-5e7f8e3f-50e2-4139-86da-9e50bafca75a
    &client_secret=WJj1uXCzB8TioC4HA8ISIQFTuwYtJK_4tqDR4JHZ92E-hTa0uMu-Gg
    
    {
      "device_code": "qF31oSsTV85uOEejoA2TZ3XHSw_0WblH6OPLmKepHzaP63VIjaRBWQ",
      "user_code": "3FB39358",
      "verification_uri": "https://visibility.amp.cisco.com/iroh/oauth2/device",
      "verification_uri_complete": "https://visibility.amp.cisco.com/iroh/oauth2/device?user_code=3FB39358",
      "expires_in": 600,
      "interval": 1
    }
    

    Then:

    • Display the user_code to the user
    • Redirect the user to either verification_uri or verification_uri_complete. For example you could use a QR Code if your device does not have a good browser with an easy to use user input.
    • Keep the device_code secret
  • Poll the request

    Every second or so you should Call the /token endpoint to try to retrieve the user credentials. Repeat that call until you get a clear answer or a clear error.

    • Pending Authorization

      While the user is going to the verification URI and authorize the client you will probably receive an Authorization Pending response:

      POST https://visibility.amp.cisco.com/iroh/oauth2/token
      Accept: application/json
      Content-Type: application/x-www-form-urlencoded
      User-Agent: ob-http
      
      client_id=client-5e7f8e3f-50e2-4139-86da-9e50bafca75a
      &client_secret=WJj1uXCzB8TioC4HA8ISIQFTuwYtJK_4tqDR4JHZ92E-hTa0uMu-Gg
      &grant_type=urn:ietf:params:oauth:grant-type:device_code
      &device_code=qF31oSsTV85uOEejoA2TZ3XHSw_0WblH6OPLmKepHzaP63VIjaRBWQ
      
      {
        "client-id": "client-5e7f8e3f-50e2-4139-86da-9e50bafca75a",
        "error_uri": "https://datatracker.ietf.org/doc/html/rfc8628#section-3.5",
        "trace_id": "8492398c-2651-43a5-9cb8-cf26ebf3628a",
        "error": "authorization_pending",
        "error_description": "Authorization pending"
      }
      
    • Client Authorized

      If the user authorize your Client you will get a successful result that will contain both an access and a refresh token. The access token can be used to call IROH1 on behalf of the user and you can retrieve a new access token from the refresh token.

      POST https://visibility.amp.cisco.com/iroh/oauth2/token
      Accept: application/json
      Content-Type: application/x-www-form-urlencoded
      User-Agent: ob-http
      
      client_id=client-5e7f8e3f-50e2-4139-86da-9e50bafca75a
      &client_secret=WJj1uXCzB8TioC4HA8ISIQFTuwYtJK_4tqDR4JHZ92E-hTa0uMu-Gg
      &grant_type=urn:ietf:params:oauth:grant-type:device_code
      &device_code=qF31oSsTV85uOEejoA2TZ3XHSw_0WblH6OPLmKepHzaP63VIjaRBWQ
      
      {
        "access_token": "eyJhbGciOiJS...",
        "scope": "profile inspect",
        "token_type": "bearer",
        "expires_in": 600,
        "refresh_token": "eyJhbGciOiJSUzI1..."
      }
      
    • Client denied or other issue

      In case the user denies the access you will get a clear error message.

      POST https://visibility.amp.cisco.com/iroh/oauth2/token
      Accept: application/json
      Content-Type: application/x-www-form-urlencoded
      User-Agent: ob-http
      
      client_id=client-e74de10b-aad1-44b1-bb5d-69b95d359ae9
      &client_secret=_cl_82jMo8z_0_476aI_nfS-mbVB0IjgVYhGE2FWiKSSjyahwLgiwA
      &grant_type=urn:ietf:params:oauth:grant-type:device_code
      &device_code=spQSBvYP6r33cxqOvnCc7YfwkVw6rkpoLMeKTYFW4-PrU-G6N7WApA
      
      {
        "client-id": "client-5e7f8e3f-50e2-4139-86da-9e50bafca75a",
        "error_uri": "https://datatracker.ietf.org/doc/html/rfc8628#section-3.5",
        "trace_id": "65ff0978-bb54-434f-b5a3-41163d6082d0",
        "error": "access_denied",
        "error_description": "The user rejected the access"
      }
      

      Note other problems could occurs, for example the user could lack sufficient privileges to authorize your client (not enough scopes for example). The device code will expires after a while, etc…

      For every error you should get a clear message that the authorization will not occur.

    • Further attempt or after expiration

      After the user either authorized, denied, or the device code expires you should get a device code not found error.

      POST https://visibility.amp.cisco.com/iroh/oauth2/token
      Accept: application/json
      Content-Type: application/x-www-form-urlencoded
      User-Agent: ob-http
      
      client_id=client-e74de10b-aad1-44b1-bb5d-69b95d359ae9
      &client_secret=_cl_82jMo8z_0_476aI_nfS-mbVB0IjgVYhGE2FWiKSSjyahwLgiwA
      &grant_type=urn:ietf:params:oauth:grant-type:device_code
      &device_code=spQSBvYP6r33cxqOvnCc7YfwkVw6rkpoLMeKTYFW4-PrU-G6N7WApA
      
      {
        "client-id": "client-e74de10b-aad1-44b1-bb5d-69b95d359ae9",
        "error_uri": "https://datatracker.ietf.org/doc/html/rfc8628#section-3.5",
        "trace_id": "85f8c12d-4e64-474d-bb79-1666a479c5fa",
        "error": "invalid_device_code",
        "error_description": "device code not found"
      }
      
  • Getting a new Access Token

    To get a new access token from a refresh token see Getting a new Access Token

Tutorial & Example Project
  • Clone Demo project
  • Create the client
    POST https://visibility.amp.cisco.com/iroh/oauth2-clients/clients
    Accept: application/json
    Content-Type: application/json
    User-Agent: ob-http
    Authorization: Bearer ${jwt}
    
    {
      "scopes": [ "profile", "inspect" ],
      "description": "Developer Doc OAuth2 Device Test Client",
      "name": "OAuth2 Developer Doc Test",
      "grants": [ "device-grant" ]
    }
    
    {
      "scopes": [
        "profile",
        "inspect"
      ],
      "description": "Developer Doc OAuth2 Device Test Client",
      "approved?": true,
      "redirects": [],
      "availability": "org",
      "password": "WJj1uXCzB8TioC4HA8ISIQFTuwYtJK_4tqDR4JHZ92E-hTa0uMu-Gg",
      "name": "OAuth2 Developer Doc Test",
      "org-id": "org-1",
      "enabled?": true,
      "grants": [
        "device-grant"
      ],
      "client-type": "confidential",
      "id": "client-5e7f8e3f-50e2-4139-86da-9e50bafca75a",
      "approval-status": "approved",
      "owner-id": "org-1-master-1",
      "created-at": "2021-07-21T13:54:04.989Z"
    }
    
  • Run the demo

    There are two demos to show how to use the Device Grant flow with IROH1. The code source of both should be short enough to be easy to understand.

    • In browser demo

      Edit the site/device-flow.html file to change the value of the variables SX, CLIENT_ID and CLIENT_SECRET to match the values for your client. Open the file site/device-flow.html in your browser and follow the instructions.

    • Shell script demo

      There is a easy to read shell script demo you can launch. Edit the shell script with your client credentials and run the script.

      ./device-flow-demo.sh
      

OAuth2 Metadata

You can get a more in-depth information via the OAuth 2.0 Authorization Server Metadata (see RFC8414) at:

https://visibility.amp.cisco.com/.well-known/index.html#/OAuth2/get__well_known_oauth_authorization_server

Here is an example of metadata value:

{
    "device_authorization_endpoint":
      "https://visibility.amp.cisco.com/iroh/oauth2/device_authorization",
    "grant_types_supported":
      ["authorization_code",
       "client_credential",
       "urn:ietf:params:oauth:grant-type:device_code"],
    "authorization_endpoint":
      "https://visibility.amp.cisco.com/iroh/oauth2/authorize",
    "scopes_supported":
      ["telemetry", "integration", "private-intel", "admin",
       "cognitive", "profile", "inspect", "asset", "feedback",
       "sse", "registry", "users", "investigation", "invite",
       "casebook", "orbital", "enrich", "oauth", "global-intel",
       "response", "notification", "webhook"],
    "issuer": "IROH Auth",
    "code_challenge_methods_supported": ["S256", "plain"],
    "response_types_supported": ["code", "token"],
    "token_endpoint": "https://visibility.amp.cisco.com/iroh/oauth2/token",
    "jwks_uri": "https://visibility.amp.cisco.com/.well-known/jwks",
    "service_documentation": "https://visibility.amp.cisco.com/iroh/doc/iroh-auth/index.html"
}

You want IROH to handle users

If you want IROH1 to manage your users, your API can simply accept JWT generated by IROH1. You can do that by using OpenId Connect and directly use the JWT as Bearer tokens.

JWT Checks

Your API must perform some checks on the JWT it receives but no more than necessary.

Mandatory Checks

Here is an exhaustive list of checks to perform on every JWT:

  1. Verify the signature. You can get the public key to verify the JWT signature at: https://visibility.amp.cisco.com/.well-known/index.html#/Metadata/get__well_known_jwks
  2. Verify standard expiration dates. The JWT will contain the claims exp and nbf. So you should compare them to your local time. Ensure you do not have too much clock skew. There is also an iat claims, but you do not need to perform any check about it, this will not improve the security. Just checking exp and nbf is enough.
  3. Verify there is a sub claim. Check it is identical to the claim https://schemas.cisco.com/iroh/identity/claims/user/id
  4. Verify the claim jti exists
  5. Token Kind; The claim https://schemas.cisco.com/iroh/identity/claims/oauth/kind must be either: session-token or access-token. Any other value must be rejected to access your API.
  6. Org Id; The claim https://schemas.cisco.com/iroh/identity/claims/org/id must be present.
  7. Email; The claims https://schemas.cisco.com/iroh/identity/claims/user/email must be present. Note, in some rare case it might not contain a valid email address, but the field must exists.
  8. If you need to handle specific authorizations you must only use claim: https://schemas.cisco.com/iroh/identity/claims/scopes You MUST NOT use the role claim.
Audiences

While optional, we recommend to configure your OAuth2 Client with some audience. Audience configuration is restricted and should be approved by the IROH1 team.

For your consumption of the JWT, it means, checking that the aud claim (which is a list of strings) contains the audience you expect. If you receive a JWT without this audience it is an indication that the JWT was meant to be used by another API.

Common Issues
  • Keep in mind is that the certificate used to sign the JWT can change. You should check the JWKS endpoint regularly.
  • do not check the value of the claim iss (issuer) this could change for any reason, it does not necessarily contain a specific string nor URL.
  • never use the role of a user to handle authorizations (access rights). This must only use the scopes because admin can grant some authorizations to non-admin users.
  • The scopes allowed to the current JWT are specified in the claim https://schemas.cisco.com/iroh/identity/claims/scopes When decoding the JWT there are other lists of scopes which should not be mandatory. So for authorization you should not look at either:
    • https://schemas.cisco.com/iroh/identity/claims/user/scopes
    • https://schemas.cisco.com/iroh/identity/claims/oauth/scopes

OpenID Connect

OpenID Connect is an identity layer on top of OAuth2. Follow the instructions of the only difference are:

  1. Your client must contain the scope openid.
  2. The response from the /token will contain not only an access token and a refresh token but also an id_token.
  3. The scopes in the access and refresh tokens will behave as if the field of your client allow-partial-user-scopes was true. The scopes will be the biggest intersection between:

    • the user scopes
    • the client scopes
    • the requested scopes

⚠ Verify your integration follow the Client Checklist.

OpenID Connect full example

You can find the specifications of OpenID Connect here. And in particular: OpenID Connect using Authorization Code Grant Flow

This provides the ability for a user to both grant access to your web application and retrieve user identity information into your application.

With this workflow the user will not need to copy/paste any credentials. He just needs to click on a button to authorize your client.

To give an overview of the process on how to integrate:

  1. Create a single client for your application.
  2. Generate a link on your web application. When the user clicks on it, he is redirected to IROH1, and he can grant access to your application. The user returns to your website with a secret code and you will then retrieve both a refresh token that you should keep securely and an id token.
  3. With the refresh token you can retrieve an access token without user interaction.
  4. The access token can be used to call the IROH1 APIs on behalf of the user.

Notes:

  • Step 1 should be done only once.
  • Step 2 should be done only once per user.
  • Step 3 should be done only when you want to call the API and the access token has expired (every 10 min by default).
  • Step 4 should be done for all API calls, so it is highly recommended to cache the access tokens and not to call the refresh token for each API call to IROH1.
Create a Client

You can create the client via the Admin interface in SecureX or via the API directly.

IMPORTANT: Add the openid scope to your client. This will declare the client as an OpenID Connect compatible client.

Notes:

  • before creating the client, you must set its field grants to ["auth-code"] in order to support Authorization Code Grant workflow.
  • If you want to be autonomous, you’ll need to have control of an HTTPS URL (generally something along https://my.domain.tld/path/to/callback). This is the URL you’ll use to set in the redirects value of the client.

Depending on the information you’ll provide during client creation, the client won’t be automatically approved. The client will be approved during creation if all the following conditions are met:

For more details about each field refer to the section OAuth2 Client Model. To create a client refer to the section Via the Swagger UI Interface.

  • client for all IROH1 users

    If you need to open your web application to all users and not only to the members of your organization you will need to create a client with the availability field equal to everyone. The client won’t be approved and you’ll need to reach out to a IROH1 administrator to approve it.

  • redirects to localhost without https

    Quite often during the development phase you’ll need to use your client locally. As such, you’ll want to add a URL in the redirects array like http://localhost:*/callback. In that case you will also need to reach out to an admin to approve clients that do not use the HTTPS scheme.

  • public clients

    If your application is a frontend only application, having a password does not make sense. For this specific case it is recommended (by the RFC) to not use the Implicit grant, instead use an Authorization Code Grant client without any password. In this case you must set the client-type to public.

    This is also a very specific need. You will need to request approval from a IROH1 administrator.

  • Token lifetime

    The current default lifetime of access token is 10 minutes. The current default lifetime for refresh token is unlimited.

    If you want longer lived access token or shorter lived refresh token you simply need to set the fields access-token-lifetime-in-sec and refresh-token-lifetime-in-sec when creating your client. You will need to request a IROH1 administrator approve your client.

    ☞ After a refresh-token lifetime, the refresh token will not work anymore. After half the lifetime, when getting a new access token you will also get a newer refresh token with the same lifetime. It is then up to you, the client, to perform a new refresh token call during the second half or 1h before the expiration to get a new refresh token. After a new refresh-token is provided, the old one can be revoked anytime so you should cease using the old refresh token.

    Without a valid refresh token you’ll be forced to reengage the user in an interactive authorization workflow.

Use the Client

☞ If you have not yet created a client that can be used for Authorization Code Grant, please refer to the section Create a Client.

If you have created a client and it is approved. You should have the following information:

  • client-id
  • client-password
  • redirect-uri; one of the URL you set in the redirects array of the client
  • scopes; the list of scope you’ll ask IROH1 users to grant to your app. If it is not empty, it must contain openid.

You will also need to know the authorization and token URLs of IROH1. You can get them at the metadata endpoint (c.f. OAuth2 Metadata).

  • Present a URL to the User

    The first step is to present the user with a URL (return line here for readability):

    https://visibility.amp.cisco.com/iroh/oauth2/authorization?
       response_type=code
       & client_id=YOUR_CLIENT_ID
       & redirect_uri=YOUR_REDIRECT_URL
       & scope=LIST_OF_SCOPES_SPACE_SEPARATED
       & state=A_DIFFICULT_TO_GUESS_RANDOM_STRING
    

    The scope query parameter is optional. Without it the scopes will match those of the client. Once the user clicks on the link they will be redirected to IROH1 and asked to grant access to your web application. If the user refuses or accepts, the user will be redirected to the redirecturi with parameters that will contain further information.

  • Getting the Refresh Token

    You should listen to your redirect_uri and parse the query parameters. An example of URL the user will be redirected to after granting your app the access would be:

    YOUR_REDIRECT_URL?state=A_DIFFICULT_TO_GUESS_RANDOM_STRING&code=SOME_CODE
    

    Then you must verify the state is the same one the user has clicked on. (In our example state must be A_DIFFICULT_TO_GUESS_RANDOM_STRING).

    You can then get the refresh token using the code by making a POST request to .

    curl -XPOST \
         -u "YOUR_CLIENT_ID:YOUR_CLIENT_PASSWORD" \
         -H "Content-Type: application/x-www-form-urlencoded" \
         -d "grant_type=authorization_code&redirect_uri=YOUR_REDIRECT_URL&code=SOME_CODE" \
         https://visibility.amp.cisco.com/iroh/oauth2/token
    

    The `-u` is to use Basic Auth, with client-id as user and client-password as password. It is also very important to use the content type application/x-www-form-urlencoded. Note that the route does not support parameters sent via a JSON content type. (cf. RFC6749 section 4.1.3)

    The server should then return a successful response (cf. RFC6749 section 4.1.4):

      {"access_token":"eyJhbG...",
       "id_token":"eyJhbG...",
       "token_type":"bearer",
       "expires_in":600,
       "refresh_token":"eJJyFFA..."}
    

    IMPORTANT: The POST to the /token route exposes your client password, so it must be done server side not inside the browser.

    You should have both an access token and a refresh token. The access token will expire in 600 seconds. But the refresh token shouldn’t expire soon. Take care of saving the refresh token for your user. Each time you’ll need a new fresh access token you’ll need the refresh token.

    Using the access token you can make calls to the IROH1 API on behalf of the user but will be restricted to the scope they granted your app.

    An example of a call to the API would be to retrieve the user profile (for that your client should have the scope “profile” in the list of scopes).

    > access_token="eyJhbG..."
    > curl -XGET \
           -H "Accept: application/json" \
           -H "Authorization: Bearer $access_token" \
           https://visibility.amp.cisco.com/iroh/profile/whoami
    
  • Getting a new Access Token

    After a few minutes, the access token will expire. API access after token expiration will require user interaction. You will only need to use the refresh token of the user to get a new access token.

    The call to refresh a token uses the same endpoint you used to get it the first time. But instead of using a grant_type of authorization_code, you will use a grant_type equal to refresh_token (cf. RFC6749 section 6).

    curl -XPOST \
         -u "YOUR_CLIENT_ID:YOUR_CLIENT_PASSWORD" \
         -H "Content-Type: application/x-www-form-urlencoded" \
         -d "grant_type=refresh_token&refresh_token=REFRESH_TOKEN" \
         https://visibility.amp.cisco.com/iroh/oauth2/token
    

    The server should then return a successful response:

      {"access_token":"eyJhbG...",
       "token_type":"bearer",
       "expires_in":600}
    

    IMPORTANT: The response might also contain a new refresh token along with the new access token. In that case you must revoke the old refresh token and use the new one instead. IROH1 might accept the old refresh token for a short period to prevent bugs in distributed environments.

Tutorial

This section provides complete example. The credentials used will not be functional, but you could be able to follow all the steps.

  • Clone Demo project

    Clone https://github.com/yogsototh/oauth2-client-demo

    Serve it to your own domain, possibly in some directory. This tutorial assumes it will be: https://my.domain.tld/ctr-oauth/

    If you cannot test directly from your domain and want to test on localhost check the Run on localhost section.

  • Create the client
    POST https://visibility.amp.cisco.com/iroh/oauth2-clients/clients
    Accept: application/json
    Content-Type: application/json
    User-Agent: ob-http
    Authorization: Bearer eyJhbG...
    
    {
      "scopes": [ "profile", "inspect", "openid" ],
      "description": "Developer Doc OAuth2 Test Client",
      "redirects": [ "https://my.domain.tld/ctr-oauth/callback.html" ],
      "availability": "org",
      "name": "OAuth2 Developer Doc Test",
      "grants": [ "auth-code" ],
      "client-type": "confidential"
    }
    
    {
      "scopes": [
        "profile",
        "inspect",
        "openid"
      ],
      "description": "Developer Doc OAuth2 Test Client",
      "approved?": true,
      "redirects": [
        "https://my.domain.tld/ctr-oauth/callback.html"
      ],
      "availability": "org",
      "password": "CrXwg31_vnRHpjPXzgVzUFKHr6RO8GTL-iI8aDeUU3n48NtD7PFLhg",
      "name": "OAuth2 Developer Doc Test",
      "org-id": "f47a89bf-5d2e-4392-b770-000000000000",
      "enabled?": true,
      "grants": [
        "auth-code"
      ],
      "client-type": "confidential",
      "id": "client-3bb1e787-381d-4f12-bf32-e1158f200ddc",
      "approval-status": "approved",
      "owner-id": "f0010924-e1bc-4b03-b600-000000000000",
      "created-at": "2019-07-25T14:15:29.117Z"
    }
    
  • Configure the demo

    Edit the info.js to look like:

    var oauthURLPrefix="https://visibility.amp.cisco.com";
    var oauthServerUrl=oauthURLPrefix + "/iroh/oauth2/authorize";
    var oauthServerTokenUrl=oauthURLPrefix + "/iroh/oauth2/token";
    var resourceProviderTestEndpoint=oauthURLPrefix + "/iroh/profile/whoami" ;
    var response_type="code";
    var client_id="client-3bb1e787-381d-4f12-bf32-e1158f200ddc";
    var client_password = "CrXwg31_vnRHpjPXzgVzUFKHr6RO8GTL-iI8aDeUU3n48NtD7PFLhg";
    var redirect_uri="https://my.domain.tld/ctr-oauth/callback.html";
    var scopes=[ "profile", "inspect", "openid" ];
    var scope=scopes.join(" ");
    var state="whatever=";
    
  • Run the demo
    1. Visit https://my.domain.tld/ctr-oauth/index.html
    2. Click on the link, you will be redirected to IROH1
    3. Authorize the client
    4. Now back at https://my.domain.tld/ctr-oauth/callback.html you should inspect the call in your browser and play with the demo.
    5. Retrieve the access token and refresh token using the code.
    6. Get another access token using the refresh token.
  • Details about queries in the demo

    While the workflow necessitates user interaction through the browser, we will describe most call in detail.

    • Code in callback URL

      The user interaction is only needed to authorize the client. He will then visit https://my.domain.tld/ctr-oauth/callback.html with the query parameters code and state.

      Here is a real example:

      https://my.domain.tld/ctr-oauth/callback.html?
          code=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL2VtYWlsIjoieWFlc3Bvc2lAY2lzY28uY29tIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9zY29wZXMiOlsiaXJvaC1hZG1pbiIsImludGVncmF0aW9uIiwicHJpdmF0ZS1pbnRlbCIsImFkbWluIiwicHJvZmlsZSIsImluc3BlY3QiLCJpcm9oLWF1dGgiLCJzc2UiLCJ1c2VycyIsImNpc2NvIiwiY2FzZWJvb2siLCJvcmJpdGFsIiwiZW5yaWNoIiwib2F1dGgiLCJnbG9iYWwtaW50ZWwiLCJjb2xsZWN0IiwicmVzcG9uc2UiLCJ1aS1zZXR0aW5ncyJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25pY2siOiJZYW5uIEVzcG9zaXRvIiwiZW1haWwiOiJ5YWVzcG9zaUBjaXNjby5jb20iLCJpc3MiOiJJUk9IIEF1dGgiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJleHAiOjE1NjQxMjk4NDcsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3VzZXJcL2lkIjoiZjAwMTA5MjQtZTFiYy00YjAzLWI2MDAtODljNmNmNTI3NTdjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vcmdcL2lkIjoiZjQ3YTg5YmYtNWQyZS00MzkyLWI3NzAtYWQ0ODIxYTgyYWNmIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwvZ3JhbnQiOiJhdXRoLWNvZGUiLCJqdGkiOiJmMjFiMDU5NS04YzhjLTRkZTktOGI4MS01NWVmZTIwMThlODMiLCJuYmYiOjE1NjQxMjkxODcsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25hbWUiOiJZYW5uIEVzcG9zaXRvIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9pZCI6ImYwMDEwOTI0LWUxYmMtNGIwMy1iNjAwLTg5YzZjZjUyNzU3YyIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL2NsaWVudFwvaWQiOiJjbGllbnQtM2JiMWU3ODctMzgxZC00ZjEyLWJmMzItZTExNThmMjAwZGRjIiwiaWF0IjoxNTY0MTI5MjQ3LCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL29hdXRoXC9raW5kIjoiY29kZS10b2tlbiJ9.IL5xGEfcwnKpd2KNMyo2_kNtkEsfwTa6vYKJHR6uF3L3CGX7edat_eYXNmLACgamX3pdYfXo8JAHEOvdr7zl6-fLq0SHlFdxy4pbVQbr8gJt6TAZVWgNu8wxJfTzL6-V0oIv6mcYD2NWTXYoA8R19u6iFnOtoTwXVx58Dk2UXQuueHmq2CejpJspGgjChAHsebh1XKl-6uPo7debCQvrzPhB56lGShKT0NgEqT_ZML032iFqGIBHUe6WdlqO_i6ji1-QX3CPmA_rwep7n0h1RuDxWib9YCtwJFh0SO0AQX_bK-0000000000000000000000000000000000000000
        & state=y5jaXNjby5jb21cL2lyb2hcL2lkZW
      

      The code represents an agreement from a IROH1 user to grant access to your application. Remember OAuth2 Authorization Code Grant is a kind of contract between 3 entities: your application (the client), the user, and IROH1. The code query parameter is a JWT. Once decoded it contains:

      {
        "https://schemas.cisco.com/iroh/identity/claims/user/email":
          "dev.null@cisco.com",
        "https://schemas.cisco.com/iroh/identity/claims/user/scopes": [
          "integration",
          "private-intel",
          "admin",
          "profile",
          "inspect",
          "sse",
          "users",
          "casebook",
          "enrich",
          "oauth",
          "global-intel:read",
          "collect",
          "response",
          "ui-settings"
        ],
        "https://schemas.cisco.com/iroh/identity/claims/user/nick":
          "Yann Esposito",
        "email": "dev.null@cisco.com",
        "iss": "IROH Auth",
        "https://schemas.cisco.com/iroh/identity/claims/scopes": [
          "profile",
          "inspect",
          "openid"
        ],
        "exp": 1564129847,
        "https://schemas.cisco.com/iroh/identity/claims/oauth/user/id":
          "f0010924-e1bc-4b03-b600-000000000000",
        "https://schemas.cisco.com/iroh/identity/claims/org/id":
          "f47a89bf-5d2e-4392-b770-000000000000",
        "https://schemas.cisco.com/iroh/identity/claims/oauth/grant":
          "auth-code",
        "jti": "f21b0595-8c8c-4de9-8b81-55efe2018e83",
        "nbf": 1564129187,
        "https://schemas.cisco.com/iroh/identity/claims/oauth/scopes": [
          "profile",
          "inspect"
        ],
        "https://schemas.cisco.com/iroh/identity/claims/user/name":
          "Yann Esposito",
        "https://schemas.cisco.com/iroh/identity/claims/user/id":
          "f0010924-e1bc-4b03-b600-000000000000",
        "https://schemas.cisco.com/iroh/identity/claims/oauth/client/id":
          "client-3bb1e787-381d-4f12-bf32-000000000000",
        "iat": 1564129247,
        "https://schemas.cisco.com/iroh/identity/claims/oauth/kind":
          "code-token"
      }
      

      The JWT claims are detailed in the Call the API / JWT section. Note that the JWT contains 3 different claims related to scopes:

      Another field to note is the oauth/kind that contains code-token. So this JWT can only be used as a temporary code during OAuth2 workflow.

      Notice also the expiration date (exp) is just 10 minutes after the creation date (iat).

      IMPORTANT Your client must check that the state parameter is the same as the state parameter in the link the user clicked on. Of course each time you present a link to grant your app to a user, you should generate a new strong random string.

    • Get the access and refresh token with the code

      With this code token (you don’t need to decode it) you can get a refresh token and an access token.

      POST https://visibility.amp.cisco.com/iroh/oauth2/token
      Accept: application/json
      Content-Type: application/x-www-form-urlencoded; charset=UTF-8
      User-Agent: ob-http
      Authorization: Basic Y2xpZW50LTNiYjFlNzg3LTM4MWQtNGYxMi1iZjMyLWUxMTU4ZjIwMGRkYzpDclh3ZzMxX3ZuUkhwalBYemdWelVGS0hyNlJPOEdUTC1pSThhRGVVVTNuNDhOdEQ3UEZMaGc=
      
      code=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL2VtYWlsIjoieWFlc3Bvc2lAY2lzY28uY29tIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9zY29wZXMiOlsiaXJvaC1hZG1pbiIsImludGVncmF0aW9uIiwicHJpdmF0ZS1pbnRlbCIsImFkbWluIiwicHJvZmlsZSIsImluc3BlY3QiLCJpcm9oLWF1dGgiLCJzc2UiLCJ1c2VycyIsImNpc2NvIiwiY2FzZWJvb2siLCJvcmJpdGFsIiwiZW5yaWNoIiwib2F1dGgiLCJnbG9iYWwtaW50ZWwiLCJjb2xsZWN0IiwicmVzcG9uc2UiLCJ1aS1zZXR0aW5ncyJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25pY2siOiJZYW5uIEVzcG9zaXRvIiwiZW1haWwiOiJ5YWVzcG9zaUBjaXNjby5jb20iLCJpc3MiOiJJUk9IIEF1dGgiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJleHAiOjE1NjQxMzA5NTAsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3VzZXJcL2lkIjoiZjAwMTA5MjQtZTFiYy00YjAzLWI2MDAtODljNmNmNTI3NTdjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vcmdcL2lkIjoiZjQ3YTg5YmYtNWQyZS00MzkyLWI3NzAtYWQ0ODIxYTgyYWNmIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwvZ3JhbnQiOiJhdXRoLWNvZGUiLCJqdGkiOiI2NWU4MGE0Yi03YTM2LTQzNDEtOGI0NC0zZTM2MTk4YjRiMTQiLCJuYmYiOjE1NjQxMzAyOTAsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25hbWUiOiJZYW5uIEVzcG9zaXRvIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9pZCI6ImYwMDEwOTI0LWUxYmMtNGIwMy1iNjAwLTg5YzZjZjUyNzU3YyIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL2NsaWVudFwvaWQiOiJjbGllbnQtM2JiMWU3ODctMzgxZC00ZjEyLWJmMzItZTExNThmMjAwZGRjIiwiaWF0IjoxNTY0MTMwMzUwLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL29hdXRoXC9raW5kIjoiY29kZS10b2tlbiJ9.yrOZloNpYGrs0eIWk9dVl-OoSgnSggTcCGrS-iPyhsefZ48SFeoWYtiktki8-uRi6hCjYwQ7WMOQ-MQ9Fr7w4ELMNZJeBSHZjHFty-wH_Fmx62NkVzdAty9pALD2sgqBMS44NWPlyzni1h7sP7wRRxr70o4XdPZUD-wIyjmM0Ewtf0tB-MEPfdXm51na-xt3RAd_hoCP3CubuSPYCDp9dQ-9AOzAAJfogRGboI_u1I38oMY_x_GRZUdyLdOHvVz4XaoBUfRgwS-C7eVu73ULavfJmd5W4UaO_LNV9oNgxQQD9B9JlLs7dMVzFxe6UaYi8KXUdk-qZRD5Px_lth7uVw
      &redirect_uri=https://my.domain.tld/ctr-oauth/callback.html
      &scope=profile%33inspect
      &grant_type=authorization_code
      
      {
        "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL2VtYWlsIjoieWFlc3Bvc2lAY2lzY28uY29tIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9zY29wZXMiOlsiaXJvaC1hZG1pbiIsImludGVncmF0aW9uIiwicHJpdmF0ZS1pbnRlbCIsImFkbWluIiwicHJvZmlsZSIsImluc3BlY3QiLCJpcm9oLWF1dGgiLCJzc2UiLCJ1c2VycyIsImNpc2NvIiwiY2FzZWJvb2siLCJvcmJpdGFsIiwiZW5yaWNoIiwib2F1dGgiLCJnbG9iYWwtaW50ZWwiLCJjb2xsZWN0IiwicmVzcG9uc2UiLCJ1aS1zZXR0aW5ncyJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25pY2siOiJZYW5uIEVzcG9zaXRvIiwiZW1haWwiOiJ5YWVzcG9zaUBjaXNjby5jb20iLCJzdWIiOiJmMDAxMDkyNC1lMWJjLTRiMDMtYjYwMC04OWM2Y2Y1Mjc1N2MiLCJpc3MiOiJJUk9IIEF1dGgiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJleHAiOjE1NjQxMzA5NzMsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3VzZXJcL2lkIjoiZjAwMTA5MjQtZTFiYy00YjAzLWI2MDAtODljNmNmNTI3NTdjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vcmdcL2lkIjoiZjQ3YTg5YmYtNWQyZS00MzkyLWI3NzAtYWQ0ODIxYTgyYWNmIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwvZ3JhbnQiOiJhdXRoLWNvZGUiLCJqdGkiOiI1Y2Y1NGRkMi0zMmNlLTRkOTctODEzYy0wZWFiYzZlZWI1MjEiLCJuYmYiOjE1NjQxMzAzMTMsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25hbWUiOiJZYW5uIEVzcG9zaXRvIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9pZCI6ImYwMDEwOTI0LWUxYmMtNGIwMy1iNjAwLTg5YzZjZjUyNzU3YyIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL2NsaWVudFwvaWQiOiJjbGllbnQtM2JiMWU3ODctMzgxZC00ZjEyLWJmMzItZTExNThmMjAwZGRjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC92ZXJzaW9uIjoidjEuMjAuMC1kZjgxNGU3YjYyODMwZGRkNTc2ZCIsImlhdCI6MTU2NDEzMDM3MywiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwva2luZCI6ImFjY2Vzcy10b2tlbiJ9.tZ75vCYYst9gSbnZl7LZ3GiVXRpAmOFy9cmUs9wGkSw7XSdRN-4sqwUkcCoNq161piZedf17PrGRMurEtq9x8cW1_YuU_5pi9mDMH1ayQYNXJXkk6G_PJTbzyBLosdp3IAc_GN7DrCZoHx9-oveCy0MfXTbgrjtsx7BoELynECbjTPAfi8CU_rPRCnIyrxLTFMJdqLHsUVP_MDyXEvV43EPw1uRUn9ZBf-dBAswdFsEhH8RF4IFtZyDxb49HTpoWHA54jFWiYAP3cISyS6IKI8H1aVQHAUDbdSzunx0Rp5U9xJysZiAZzVkiSUGTJqXQt_00000000000000000000",
        "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL2VtYWlsIjoieWFlc3Bvc2lAY2lzY28uY29tIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9zY29wZXMiOlsiaXJvaC1hZG1pbiIsImludGVncmF0aW9uIiwicHJpdmF0ZS1pbnRlbCIsImFkbWluIiwicHJvZmlsZSIsImluc3BlY3QiLCJpcm9oLWF1dGgiLCJzc2UiLCJ1c2VycyIsImNpc2NvIiwiY2FzZWJvb2siLCJvcmJpdGFsIiwiZW5yaWNoIiwib2F1dGgiLCJnbG9iYWwtaW50ZWwiLCJjb2xsZWN0IiwicmVzcG9uc2UiLCJ1aS1zZXR0aW5ncyJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25pY2siOiJZYW5uIEVzcG9zaXRvIiwiZW1haWwiOiJ5YWVzcG9zaUBjaXNjby5jb20iLCJzdWIiOiJmMDAxMDkyNC1lMWJjLTRiMDMtYjYwMC04OWM2Y2Y1Mjc1N2MiLCJpc3MiOiJJUk9IIEF1dGgiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJleHAiOjE1NjQxMzA5NzMsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3VzZXJcL2lkIjoiZjAwMTA5MjQtZTFiYy00YjAzLWI2MDAtODljNmNmNTI3NTdjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vcmdcL2lkIjoiZjQ3YTg5YmYtNWQyZS00MzkyLWI3NzAtYWQ0ODIxYTgyYWNmIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwvZ3JhbnQiOiJhdXRoLWNvZGUiLCJqdGkiOiI1Y2Y1NGRkMi0zMmNlLTRkOTctODEzYy0wZWFiYzZlZWI1MjEiLCJuYmYiOjE1NjQxMzAzMTMsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25hbWUiOiJZYW5uIEVzcG9zaXRvIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9pZCI6ImYwMDEwOTI0LWUxYmMtNGIwMy1iNjAwLTg5YzZjZjUyNzU3YyIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL2NsaWVudFwvaWQiOiJjbGllbnQtM2JiMWU3ODctMzgxZC00ZjEyLWJmMzItZTExNThmMjAwZGRjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC92ZXJzaW9uIjoidjEuMjAuMC1kZjgxNGU3YjYyODMwZGRkNTc2ZCIsImlhdCI6MTU2NDEzMDM3MywiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwva2luZCI6ImFjY2Vzcy10b2tlbiJ9.tZ75vCYYst9gSbnZl7LZ3GiVXRpAmOFy9cmUs9wGkSw7XSdRN-4sqwUkcCoNq161piZedf17PrGRMurEtq9x8cW1_YuU_5pi9mDMH1ayQYNXJXkk6G_PJTbzyBLosdp3IAc_GN7DrCZoHx9-oveCy0MfXTbgrjtsx7BoELynECbjTPAfi8CU_rPRCnIyrxLTFMJdqLHsUVP_MDyXEvV43EPw1uRUn9ZBf-dBAswdFsEhH8RF4IFtZyDxb49HTpoWHA54jFWiYAP3cISyS6IKI8H1aVQHAUDbdSzunx0Rp5U9xJysZiAZzVkiSUGTJqXQt_00000000000000000000",
        "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL2VtYWlsIjoieWFlc3Bvc2lAY2lzY28uY29tIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9zY29wZXMiOlsiaXJvaC1hZG1pbiIsImludGVncmF0aW9uIiwicHJpdmF0ZS1pbnRlbCIsImFkbWluIiwicHJvZmlsZSIsImluc3BlY3QiLCJpcm9oLWF1dGgiLCJzc2UiLCJ1c2VycyIsImNpc2NvIiwiY2FzZWJvb2siLCJvcmJpdGFsIiwiZW5yaWNoIiwib2F1dGgiLCJnbG9iYWwtaW50ZWwiLCJjb2xsZWN0IiwicmVzcG9uc2UiLCJ1aS1zZXR0aW5ncyJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25pY2siOiJZYW5uIEVzcG9zaXRvIiwiZW1haWwiOiJ5YWVzcG9zaUBjaXNjby5jb20iLCJpc3MiOiJJUk9IIEF1dGgiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJleHAiOjE3NTE0MTQ0MDAwLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL29hdXRoXC91c2VyXC9pZCI6ImYwMDEwOTI0LWUxYmMtNGIwMy1iNjAwLTg5YzZjZjUyNzU3YyIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb3JnXC9pZCI6ImY0N2E4OWJmLTVkMmUtNDM5Mi1iNzcwLWFkNDgyMWE4MmFjZiIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL2dyYW50IjoiYXV0aC1jb2RlIiwianRpIjoiMjc0YjkwYTAtZDlhNi00YmI2LWJiN2UtMDkzYTY4MzllMTUyIiwibmJmIjoxNTY0MTMwMzEzLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL29hdXRoXC9zY29wZXMiOlsicHJvZmlsZSIsImluc3BlY3QiXSwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9uYW1lIjoiWWFubiBFc3Bvc2l0byIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvdXNlclwvaWQiOiJmMDAxMDkyNC1lMWJjLTRiMDMtYjYwMC04OWM2Y2Y1Mjc1N2MiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL29hdXRoXC9jbGllbnRcL2lkIjoiY2xpZW50LTNiYjFlNzg3LTM4MWQtNGYxMi1iZjMyLWUxMTU4ZjIwMGRkYyIsImlhdCI6MTU2NDEzMDM3MywiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwva2luZCI6InJlZnJlc2gtdG9rZW4ifQ.abtBzZmX64XDTB7OJkcyCsOrGnXj-c6tSg89qg9ENnHSeBs-e4AhQMzHO5ZF5Cb4e7c8z34k0gxatuBqLTtLTztV32ktJGZ1IwFA4k7bpPrW1qdrSJQfZjOBBdLG3DSDMqo7dihqh_4VkfZGHDUNfao12xHTXCbzzfKiZNr6f8UJ0lzEvNZ2tQFkIMjdLVrN7OSk5K56-4SwEL6-X7LHRjF8M0FrGq9QU2lztuRyGXfbPOIal4wJdcfZ-Z2S1_fC82y-rE2mEDEX-diCI_oC_01wFiBbklJLoUEYK6Ry0000000000000000000000000000000000000000000000",
        "token_type": "bearer",
        "expires_in": 600,
        "scope": "profile inspect"
      }
      

      Notes:

      • The Authorization header is for Basic Auth, so it contains Basic followed by a base64 encoded CLIENT-ID:CLIENT-PASSWORD.
      • The body is not JSON. The Content-Type is application/x-www-form-urlencoded.
      • The access token will expires in 600 seconds.
      • Both the access_token and refresh_token are JWT.
    • Refresh Token

      The refresh token is a JWT that once decoded contains:

      {
        "https://schemas.cisco.com/iroh/identity/claims/user/email":
          "dev.null@cisco.com",
         "https://schemas.cisco.com/iroh/identity/claims/user/nick":
          "Yann Esposito",
        "email": "dev.null@cisco.com",
        "iss": "IROH Auth",
        "https://schemas.cisco.com/iroh/identity/claims/scopes": [
          "profile",
          "inspect"
        ],
        "exp": 17514144000,
        "https://schemas.cisco.com/iroh/identity/claims/oauth/user/id":
          "f0010924-e1bc-4b03-b600-000000000000",
        "https://schemas.cisco.com/iroh/identity/claims/org/id":
          "f47a89bf-5d2e-4392-b770-000000000000",
        "https://schemas.cisco.com/iroh/identity/claims/oauth/grant":
          "auth-code",
        "jti": "64aae4a3-b99b-4215-8f63-c8bf46a4d4b5",
        "nbf": 1564130650,
        "https://schemas.cisco.com/iroh/identity/claims/user/name":
          "Yann Esposito",
        "https://schemas.cisco.com/iroh/identity/claims/user/id":
          "f0010924-e1bc-4b03-b600-000000000000",
        "https://schemas.cisco.com/iroh/identity/claims/oauth/client/id":
          "client-3bb1e787-381d-4f12-00000000000000000",
        "iat": 1564130710,
        "https://schemas.cisco.com/iroh/identity/claims/oauth/kind":
           "refresh-token"
      }
      

      Your application must save this refresh token for this user.

    • Access Token

      The access token is also a JWT that you can use to call the API. In our example the client asked for the profile scope that grants access to the route /iroh/profile/whoami.

      GET https://visibility.amp.cisco.com/iroh/profile/whoami
      Accept: application/json
      Content-Type: application/json; charset=UTF-8
      User-Agent: ob-http
      Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL2VtYWlsIjoieWFlc3Bvc2lAY2lzY28uY29tIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9zY29wZXMiOlsiaXJvaC1hZG1pbiIsImludGVncmF0aW9uIiwicHJpdmF0ZS1pbnRlbCIsImFkbWluIiwicHJvZmlsZSIsImluc3BlY3QiLCJpcm9oLWF1dGgiLCJzc2UiLCJ1c2VycyIsImNpc2NvIiwiY2FzZWJvb2siLCJvcmJpdGFsIiwiZW5yaWNoIiwib2F1dGgiLCJnbG9iYWwtaW50ZWwiLCJjb2xsZWN0IiwicmVzcG9uc2UiLCJ1aS1zZXR0aW5ncyJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25pY2siOiJZYW5uIEVzcG9zaXRvIiwiZW1haWwiOiJ5YWVzcG9zaUBjaXNjby5jb20iLCJzdWIiOiJmMDAxMDkyNC1lMWJjLTRiMDMtYjYwMC04OWM2Y2Y1Mjc1N2MiLCJpc3MiOiJJUk9IIEF1dGgiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJleHAiOjE1NjQxMzA5NzMsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3VzZXJcL2lkIjoiZjAwMTA5MjQtZTFiYy00YjAzLWI2MDAtODljNmNmNTI3NTdjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vcmdcL2lkIjoiZjQ3YTg5YmYtNWQyZS00MzkyLWI3NzAtYWQ0ODIxYTgyYWNmIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwvZ3JhbnQiOiJhdXRoLWNvZGUiLCJqdGkiOiI1Y2Y1NGRkMi0zMmNlLTRkOTctODEzYy0wZWFiYzZlZWI1MjEiLCJuYmYiOjE1NjQxMzAzMTMsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25hbWUiOiJZYW5uIEVzcG9zaXRvIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9pZCI6ImYwMDEwOTI0LWUxYmMtNGIwMy1iNjAwLTg5YzZjZjUyNzU3YyIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL2NsaWVudFwvaWQiOiJjbGllbnQtM2JiMWU3ODctMzgxZC00ZjEyLWJmMzItZTExNThmMjAwZGRjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC92ZXJzaW9uIjoidjEuMjAuMC1kZjgxNGU3YjYyODMwZGRkNTc2ZCIsImlhdCI6MTU2NDEzMDM3MywiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwva2luZCI6ImFjY2Vzcy10b2tlbiJ9.tZ75vCYYst9gSbnZl7LZ3GiVXRpAmOFy9cmUs9wGkSw7XSdRN-4sqwUkcCoNq161piZedf17PrGRMurEtq9x8cW1_YuU_5pi9mDMH1ayQYNXJXkk6G_PJTbzyBLosdp3IAc_GN7DrCZoHx9-oveCy0MfXTbgrjtsx7BoELynECbjTPAfi8CU_rPRCnIyrxLTFMJdqLHsUVP_MDyXEvV43EPw1uRUn9ZBf-dBAswdFsEhH8RF4IFtZyDxb49HTpoWHA54jFWiYAP3cISyS6IKI8H1aVQHAUDbdSzunx0Rp5U9xJysZiAZzVkiSUGTJqXQt_00000000000000000000
      
      {
        "user": {
          "scopes": [
            "integration",
            "private-intel",
            "admin",
            "profile",
            "inspect",
            "iroh-auth",
            "sse",
            "users",
            "cisco",
            "casebook",
            "enrich",
            "oauth",
            "global-intel:read",
            "collect",
            "response",
            "ui-settings"
          ],
          "updated-at": "2019-07-25T14:53:21.721Z",
          "user-email": "dev.null@cisco.com",
          "user-name": "Yann Esposito",
          "org-id": "f47a89bf-5d2e-4392-b770-000000000000",
          "user-id": "f0010924-e1bc-4b03-b600-000000000000",
          "idp-mappings": [{
            "idp": "idb-amp",
            "user-identity-id": "f0010924-e1bc-4b03-b600-000000000000",
            "organization-id": "f47a89bf-5d2e-4392-b770-000000000000"
          }],
          "enabled?": true,
          "last-logged-at": [
            "2019-07-25T14:53:22.183Z",
            "2019-07-25T13:05:50.488Z",
            "2019-07-19T14:37:09.021Z",
            "2019-07-18T12:11:51.297Z",
            "2019-07-12T14:58:36.694Z"
          ],
          "created-at": "2018-09-21T14:52:38.079Z",
          "user-nick": "Yann Esposito"
        },
        "org": {
          "scim-status": "activated",
          "name": "Cisco - IROH Team",
          "updated-at": "2019-07-22T15:27:10.845Z",
          "enabled?": true,
          "additional-scopes": [
            "integration",
            "admin",
            "sse",
          ],
          "settings": {
            "allow-all-role-to-login": false
          },
          "id": "f47a89bf-5d2e-4392-b770-000000000000",
          "created-at": "2018-09-06T04:44:12.512Z"
        }
      }
      
    • Get a new access token with the refresh token

      After a few minutes (10 by default), the access token will expire. You’ll then need to get a new access token. You shouldn’t ask the user to authorize your application again in IROH1.

      You can get a new access token just by using the refresh token of that user and your client credentials.

      POST https://visibility.amp.cisco.com/iroh/oauth2/token
      Accept: application/json
      Content-Type: application/x-www-form-urlencoded; charset=UTF-8
      User-Agent: ob-http
      Authorization: Basic Y2xpZW50LTNiYjFlNzg3LTM4MWQtNGYxMi1iZjMyLWUxMTU4ZjIwMGRkYzpDclh3ZzMxX3ZuUkhwalBYemdWelVGS0hyNlJPOEdUTC1pSThhRGVVVTNuNDhOdEQ3UEZMaGc=
      
      refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL2VtYWlsIjoieWFlc3Bvc2lAY2lzY28uY29tIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9zY29wZXMiOlsiaXJvaC1hZG1pbiIsImludGVncmF0aW9uIiwicHJpdmF0ZS1pbnRlbCIsImFkbWluIiwicHJvZmlsZSIsImluc3BlY3QiLCJpcm9oLWF1dGgiLCJzc2UiLCJ1c2VycyIsImNpc2NvIiwiY2FzZWJvb2siLCJvcmJpdGFsIiwiZW5yaWNoIiwib2F1dGgiLCJnbG9iYWwtaW50ZWwiLCJjb2xsZWN0IiwicmVzcG9uc2UiLCJ1aS1zZXR0aW5ncyJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25pY2siOiJZYW5uIEVzcG9zaXRvIiwiZW1haWwiOiJ5YWVzcG9zaUBjaXNjby5jb20iLCJpc3MiOiJJUk9IIEF1dGgiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJleHAiOjE3NTE0MTQ0MDAwLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL29hdXRoXC91c2VyXC9pZCI6ImYwMDEwOTI0LWUxYmMtNGIwMy1iNjAwLTg5YzZjZjUyNzU3YyIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb3JnXC9pZCI6ImY0N2E4OWJmLTVkMmUtNDM5Mi1iNzcwLWFkNDgyMWE4MmFjZiIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL2dyYW50IjoiYXV0aC1jb2RlIiwianRpIjoiMjc0YjkwYTAtZDlhNi00YmI2LWJiN2UtMDkzYTY4MzllMTUyIiwibmJmIjoxNTY0MTMwMzEzLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL29hdXRoXC9zY29wZXMiOlsicHJvZmlsZSIsImluc3BlY3QiXSwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9uYW1lIjoiWWFubiBFc3Bvc2l0byIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvdXNlclwvaWQiOiJmMDAxMDkyNC1lMWJjLTRiMDMtYjYwMC04OWM2Y2Y1Mjc1N2MiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL29hdXRoXC9jbGllbnRcL2lkIjoiY2xpZW50LTNiYjFlNzg3LTM4MWQtNGYxMi1iZjMyLWUxMTU4ZjIwMGRkYyIsImlhdCI6MTU2NDEzMDM3MywiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwva2luZCI6InJlZnJlc2gtdG9rZW4ifQ.abtBzZmX64XDTB7OJkcyCsOrGnXj-c6tSg89qg9ENnHSeBs-e4AhQMzHO5ZF5Cb4e7c8z34k0gxatuBqLTtLTztV32ktJGZ1IwFA4k7bpPrW1qdrSJQfZjOBBdLG3DSDMqo7dihqh_4VkfZGHDUNfao12xHTXCbzzfKiZNr6f8UJ0lzEvNZ2tQFkIMjdLVrN7OSk5K56-4SwEL6-X7LHRjF8M0FrGq9QU2lztuRyGXfbPOIal4wJdcfZ-Z2S1_fC82y-rE2mEDEX-diCI_oC_01wFiBbklJLoUEYK6RylEQ9lIMgYsUNsw07nM3IWua2cq5O6rNWEe9yJJzo5TIuQg
      &scope=profile%33inspect
      &grant_type=refresh_token
      
      {
        "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL2VtYWlsIjoieWFlc3Bvc2lAY2lzY28uY29tIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9zY29wZXMiOlsiaXJvaC1hZG1pbiIsImludGVncmF0aW9uIiwicHJpdmF0ZS1pbnRlbCIsImFkbWluIiwicHJvZmlsZSIsImluc3BlY3QiLCJpcm9oLWF1dGgiLCJzc2UiLCJ1c2VycyIsImNpc2NvIiwiY2FzZWJvb2siLCJvcmJpdGFsIiwiZW5yaWNoIiwib2F1dGgiLCJnbG9iYWwtaW50ZWwiLCJjb2xsZWN0IiwicmVzcG9uc2UiLCJ1aS1zZXR0aW5ncyJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25pY2siOiJZYW5uIEVzcG9zaXRvIiwiZW1haWwiOiJ5YWVzcG9zaUBjaXNjby5jb20iLCJzdWIiOiJmMDAxMDkyNC1lMWJjLTRiMDMtYjYwMC04OWM2Y2Y1Mjc1N2MiLCJpc3MiOiJJUk9IIEF1dGgiLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJleHAiOjE1NjQxMzQxODksImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3VzZXJcL2lkIjoiZjAwMTA5MjQtZTFiYy00YjAzLWI2MDAtODljNmNmNTI3NTdjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vcmdcL2lkIjoiZjQ3YTg5YmYtNWQyZS00MzkyLWI3NzAtYWQ0ODIxYTgyYWNmIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwvZ3JhbnQiOiJhdXRoLWNvZGUiLCJqdGkiOiIzNmY4YzgxNi1jN2E1LTQ0ZWMtYWZhNS1iMDI4OGIzMzJkZDkiLCJuYmYiOjE1NjQxMzM1MjksImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL3Njb3BlcyI6WyJwcm9maWxlIiwiaW5zcGVjdCJdLCJodHRwczpcL1wvc2NoZW1hcy5jaXNjby5jb21cL2lyb2hcL2lkZW50aXR5XC9jbGFpbXNcL3VzZXJcL25hbWUiOiJZYW5uIEVzcG9zaXRvIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC91c2VyXC9pZCI6ImYwMDEwOTI0LWUxYmMtNGIwMy1iNjAwLTg5YzZjZjUyNzU3YyIsImh0dHBzOlwvXC9zY2hlbWFzLmNpc2NvLmNvbVwvaXJvaFwvaWRlbnRpdHlcL2NsYWltc1wvb2F1dGhcL2NsaWVudFwvaWQiOiJjbGllbnQtM2JiMWU3ODctMzgxZC00ZjEyLWJmMzItZTExNThmMjAwZGRjIiwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC92ZXJzaW9uIjoidjEuMjAuMC1kZjgxNGU3YjYyODMwZGRkNTc2ZCIsImlhdCI6MTU2NDEzMzU4OSwiaHR0cHM6XC9cL3NjaGVtYXMuY2lzY28uY29tXC9pcm9oXC9pZGVudGl0eVwvY2xhaW1zXC9vYXV0aFwva2luZCI6ImFjY2Vzcy10b2tlbiJ9.t8f5cwIBYMYlKSewpCAv2Sgh3xOhQS_MkQqFd14Xz2j9eZD_SpfLRSdYCt5glW9NMiMuUHqtzkYroK46zYeFpy5XoEx3gLZLJC4eJaVdIxRqRxt2IcJ5F7R31Xt_R5Cbkrgncl6NxynTeRyb--4UoQXlTVNmwXQxEGbM1bc9EU7iFabevJVswpj_no9Ah5zp2-BRtvszPzuF9-Ii5AUwmsAoVziH--uezFQQv5xhQNBA17fXjIn5D3oBiK50_vZEaAgdHE3SLDDPqw4BiPzeNipvE6oGuA486Bo3XET_O7DkgzzsEakwZe2HF2DKAstBLJSeMdRjA-fhBrRCLxHJSQ",
        "token_type": "bearer",
        "expires_in": 600,
        "scope": "profile inspect"
      }
      

      This is a very similar query to the one we used for code. The only differences are:

      • use refresh_token instead of code
      • use refresh_token for the grant_type parameter

      You must use Basic Auth with your client id and password, and use the application/x-www-form-urlencoded Content Type.

  • Run on localhost

    For security, IROH1 does not allow clients with non https redirect URIs to be approved automatically.

    In order to serve HTTPS locally you should follow the instructions to create a new self signed certificate.

    git clone https://github.com/yogsototh/oauth2-client-demo
    cd cert
    ./gen-new-root-cert.sh
    ./gen-new-cert.sh rootCA
    cd ..
    

    You should serve the site/ directory using the created server.crt and server.key.

    A possible tool is sws. The easiest way to install it is to first install stack and then execute stack install sws.

    You may also need to modify your browser to trust the self-signed certificate.

Checklist

⚠ Verify your integration follow the Client Checklist.

Client Checklist

⚠ IMPORTANT

As a client of IROH1 you must take care of the following details.

Do not rely only on JWT expiration date

Keep in mind that:

  • JWT can be revoked anytime.
  • Users can revoke the grant of your application.
  • IROH1 can also reduce the lifetime of JWTs.

You can alway check the validity of a JWT using the following route: https://visibility.amp.cisco.com/iroh/session/session-status

Your integration should take care of the following:

  1. Expect to get back a 401 HTTP status even from a totally valid JWT. When getting a 401 status response you should try to get a new access token.
  2. Refer to those routes to have technical details, in particular you will get the issuer of IROH1 JWTs as well as the public key you can use to verify the JWT:

    While those values might not change often, they will change. In particular we will change the JWT signing key. The issuer might change due to region changes.

The Refresh Token can change

  1. As stated in the OAuth2 RFC5, if IROH1 provides you a new refresh token, you must revoke the old one and replace it. IROH1 should accept the old refresh token for a short grace period after which your refresh token will be rejected.
  2. If you cannot retrieve a new access token from a refresh token, you must ask the user again to approve your application to get a new refresh token.

Do not rely on the JWT scopes claim

Even if you hold an access-token that contains the scopes field, you should not rely on this information to accurately attribute the permissions of a user.

IROH1 can change the JWT specific claims for certain clients, specially if there are size constraints to the JWT, and the scopes field will no longer be present.

Consider to make a HTTP GET call to /iroh/profile/scopes to get the full list of scopes for that specific user.

Footnotes:

1

IROH for Incident Response Orchestration Hub is the name of the API that drive SecureX, Threat Response, the Ribbon and other security products inside Cisco. Integrating with SecureX or Threat Response is integrating into IROH.

2

masters are IROH1 Administrators. The term master is used instead of admin to prevent confusion between IROH1 Admins and Org admins.

3

Identity Providers: for now AMP and Threatgrid.

4

There are root scopes but also Sub Scopes

5

From OAuth2 RFC ;

The authorization server MAY issue a new refresh token, in which case the client MUST discard the old refresh token and replace it with the new refresh token. The authorization server MAY revoke the old refresh token after issuing a new refresh token to the client. If a new refresh token is issued, the refresh token scope MUST be identical to that of the refresh token included by the client in the request.

Author: Cisco IROH Services

Created: 2023-02-21 Tue 16:36