Integration Developer Documentation
Table of Contents
- Introduction
- Call the API / JWT
- Data Model
- Authorization Access / Scopes
- Application Integration
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
iss
: Issuer
Issuer of the JWT. Should be equal to “IROH Auth”.
sub
: Subject
exp
Expiration Time
The expiration time of the JWT.
nbf
Not Before
The time before which the JWT is invalid. This field is useful to handle clock skew.
iat
Issued At
The time the JWT was issued.
jti
JWT ID
Unique identifier for this JWT.
email
email (optional)
Standard field for Open Id Connect.
See: https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
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
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:
- At the top level we have Orgs,
- Each Org can have many Users,
- 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 friendlyname
: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 thesettings
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 IROH1org-id
:String
; (mandatory field) a unique identifier for the org of the user across all IROH1scopes
:[String]
; (mandatory field) a list of scopesadditional-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:
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:
- is my app a website from which I control the server side?
- If the answer is yes then you should choose the OAuth2 Code Grant Integration with a confidential client: OAuth2 Authorization Code Grant Credentials (Web Applications)
- is my app a frontend only website?
- If the answer is yes then you should choose the OAuth2 Code Grant Integration with a public client: OAuth2 Authorization Code Grant Credentials (Web Applications).
- is my app a script, a plugin, a mobile client?
- In this case you should use an OAuth2 Client Grant: OAuth2 Client Credentials (scripts)
- is my app distributed into devices without a single public url or on
device with difficult input and/or now browser.
- In this case you should use the OAuth2 Device Grant: OAuth2 Device Grant
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:
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 eitherconfidential
orpublic
(no secret),grants
a list that could containauth-code
,client-creds
ordevice-grant
,redirects
a set of URIs (only forauth-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 beuser
,org
oreveryone
. This filters the user that can grant access to this client:user
only the ownerorg
only by members of the orgeveryone
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#IDTokenowner-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 arewaiting
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 clientapproval-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.
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 routesinspect
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:
- Create a single client for your application.
- 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.
- With the refresh token you can retrieve an access token without user interaction.
- 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:
availability
is either equal to"org"
or"user"
,redirects
contain only a list of https URLs,client-type
isconfidential
,scopes
contain only unrestricted scopes (see https://visibility.amp.cisco.com/iroh/oauth2-clients/index.html#!/Scopes/get_iroh_oauth2_clients_scopes to retrieve a list of unrestricted scopes),access-token-lifetime-in-sec
andrefresh-token-lifetime-in-sec
are not provided.
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 theredirects
array of the clientscopes
; 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
- Visit https://my.domain.tld/ctr-oauth/index.html
- Click on the link, you will be redirected to IROH1
- Authorize the client
- Now back at https://my.domain.tld/ctr-oauth/callback.html you should inspect the call in your browser and play with the demo.
- Retrieve the access token and refresh token using the
code
. - 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
andstate
.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. Thecode
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:
- “https://schemas.cisco.com/iroh/identity/claims/user/scopes” contains all the scopes of the user
- “https://schemas.cisco.com/iroh/identity/claims/scopes” contains the scope the user agreed to grant
- “https://schemas.cisco.com/iroh/identity/claims/oauth/scopes” contains the scopes the client asked for
Another field to note is the
oauth/kind
that containscode-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 thestate
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 encodedCLIENT-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
andrefresh_token
are JWT.
- The Authorization header is for Basic Auth, so it contains
- 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 thegrant_type
parameter
You must use Basic Auth with your client id and password, and use the
application/x-www-form-urlencoded
Content Type. - use
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
- Use your application on some device.
- The device show a short code and ask you to go to some URL (provide a link, perhaps with a QR Code)
- Go to IROH1 and verify the code is the same
- Authorize your application
- Close the browser and get back to the device.
- The device show a message, your account is connected.
From a developer standpoint
- Create and activate a IROH1 account
- Create the device grant client for your application
Your device should be able to use the client credentials. For every user:
- Make a call to IROH1 with the client credentials to retrieve:
user_code
device_code
verification_uri_complete
to send the user toverification_uri
to send the user without pre-filling the user code
- Show the user code to the user and find a way to send the user to either
verification_uri
orverification_uri_complete
. - Concurrently, poll the IROH1
/token
endpoint with the client credentials and thedevice_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.
- 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
orverification_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
- Display the
- 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" }
- Pending Authorization
- 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 variablesSX
,CLIENT_ID
andCLIENT_SECRET
to match the values for your client. Open the filesite/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
- In browser demo
OAuth2 Metadata
You can get a more in-depth information via the OAuth 2.0 Authorization Server Metadata (see RFC8414) at:
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:
- 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
- Verify standard expiration dates.
The JWT will contain the claims
exp
andnbf
. So you should compare them to your local time. Ensure you do not have too much clock skew. There is also aniat
claims, but you do not need to perform any check about it, this will not improve the security. Just checkingexp
andnbf
is enough. - Verify there is a
sub
claim. Check it is identical to the claimhttps://schemas.cisco.com/iroh/identity/claims/user/id
- Verify the claim
jti
exists - Token Kind;
The claim
https://schemas.cisco.com/iroh/identity/claims/oauth/kind
must be either:session-token
oraccess-token
. Any other value must be rejected to access your API. - Org Id;
The claim
https://schemas.cisco.com/iroh/identity/claims/org/id
must be present. - 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. - 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:
- Your client must contain the scope
openid
. - The response from the
/token
will contain not only an access token and a refresh token but also anid_token
. 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:
- Create a single client for your application.
- 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.
- With the refresh token you can retrieve an access token without user interaction.
- 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:
availability
is either equal to"org"
or"user"
,redirects
contain only a list of https URLs,client-type
isconfidential
,scopes
contain only unrestricted scopes (see https://visibility.amp.cisco.com/iroh/oauth2-clients/index.html#!/Scopes/get_iroh_oauth2_clients_scopes to retrieve a list of unrestricted scopes),access-token-lifetime-in-sec
andrefresh-token-lifetime-in-sec
are not provided.
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 toeveryone
. 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 likehttp://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
topublic
.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
andrefresh-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 theredirects
array of the clientscopes
; the list of scope you’ll ask IROH1 users to grant to your app. If it is not empty, it must containopenid
.
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
ofauthorization_code
, you will use agrant_type
equal torefresh_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
- Visit https://my.domain.tld/ctr-oauth/index.html
- Click on the link, you will be redirected to IROH1
- Authorize the client
- Now back at https://my.domain.tld/ctr-oauth/callback.html you should inspect the call in your browser and play with the demo.
- Retrieve the access token and refresh token using the
code
. - 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
andstate
.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. Thecode
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:
- “https://schemas.cisco.com/iroh/identity/claims/user/scopes” contains all the scopes of the user
- “https://schemas.cisco.com/iroh/identity/claims/scopes” contains the scope the user agreed to grant
- “https://schemas.cisco.com/iroh/identity/claims/oauth/scopes” contains the scopes the client asked for
Another field to note is the
oauth/kind
that containscode-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 thestate
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 encodedCLIENT-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
andrefresh_token
are JWT.
- The Authorization header is for Basic Auth, so it contains
- 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 thegrant_type
parameter
You must use Basic Auth with your client id and password, and use the
application/x-www-form-urlencoded
Content Type. - use
- Code in callback URL
- 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 createdserver.crt
andserver.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:
- 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.
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
- 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.
- 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:
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.
Identity Providers: for now AMP and Threatgrid.
There are root scopes but also Sub Scopes
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.