Tools API

SWITCH edu-ID provides an API that can be queried by registered services or organizations. The API is REST/JSON based and provides a few limited functions needed to deal with SWITCH edu-ID users.

The following functions are currently available via the Tools API:

How to get access to the API?

In order to get access to the API, please write an e-mail to and mention why your application could benefit from the API. The SWITCH edu-ID team then will evaluate your request and provide you with an individual set of credentials to query the API.

How to query the API?

General

The API has to be queried with standard HTTP requests via HTTPS. The content type is ignored, but preferrably it is "application/json".

Authentication

All requests have to be authenticated with HTTP Basic authentication.

Request format

Request to the API have to be made with standard HTTP methods. The following methods are currently supported:

  • GET: Retrieve whatever information is identified by the request URL.
  • POST: Add some information.
  • PUT: Update a specified object.

The generic format of the request URL is:

https://eduid.ch/api/#version#/#objectType#/#object#

  • #version#: The version number of the API. Currently, only the version 1 ("v1") is supported.
  • #objectType#: Type of objects that the request is about
  • #object#: Specific (url-encoded) identifier for an object of #objectType#

An example request thus could be:
GET https://eduid.ch/api/v1/mail/john.doe%40example.org

Request types

The following objectTypes are currently implemented:

  • mail: E-mail address
  • swissEduID: Swiss Edu-ID identifier

Response format

All responses (including errors) are returned as JSON objects.
The content-type of the HTTP response is "application/json". The character encoding is "UTF-8".
If an operation is successfull, the HTTP response code 200 is returned. Otherwise, one of the HTTP error codes below is returned.

Errors

Standard HTTP Error Codes are used to indicate errors. In particular the following error codes are supported.

  • 400 Bad request: The request had bad syntax or was inherently impossible to be satisfied.
  • 401 Unauthorized: The client has not authenticated or the credentials are not valid.
  • 403 Forbidden: The request is for something forbidden. Authorization will not help.
  • 404 Not found: The server has not found anything matching the URI given.
  • 500 Internal Error: The server encountered an unexpected condition which prevented it from fulfilling the request.
  • 501 Not implemented: The server does not support the facility required.

In addition to the standard HTTP error code, a JSON object will be returned. It has the format of.

HTTP/1.1 #HTTP-ERROR-CODE# #HTTP-ERROR-MESSAGE#

{
    "error": {
        "code": #HTTP-ERROR-CODE#,
        "message": "#API-ERROR-MESSAGE#" 
    }
}

An example error thus would be:

HTTP/1.1 404 Not Found

{
    "error": {
        "code": 404,
        "message": "E-mail address inexistent@email.address.org is not yet associated with a SWITCH edu-ID user." 
    }
}

Check if an e-mail address is already registered in an edu-ID account

GET /api/#version#/mail/#object#

Example query: GET https://eduid.ch/api/v1/mail/johnd%40gmail.com
Description: Can be used to check if a particular email address given as #object# is already associated with a SWITCH edu-ID user or not.
Important: The returned primary email address should exclusively be used for service-internal purposes like account matching. It should not be used to contact users, because they have not given their permission.
Response: An error with code 404 will returned if the email address is not known. Otherwise, the HTTP response code 200 is returned as well as JSON object containing the primary email address, name and the SHA1 hash value of the edu-ID identifier:

{
    "mail": "#PRIMARY-EMAIL-ADDRESS#",
    "givenName": "#FIRST-NAME#",
    "surname": "#LAST-NAME#",
 "swissEduID.sha1": "#SHA1-HASH-OF-EDU-ID-IDENTIFIER#" }

Thus an example response would be:

{
    "mail": "john.doe@example.org",
    "givenName": "John",
    "surname": "Doe",
"swissEduID.sha1": "2ce6087ed05fa1c780e9503f21ba7a1917e12345" }

Set the last login time for a user's edu-ID account

PUT /api/#version#/mail/#object# 

or

PUT /api/#version#/swissEduID/#swissEduID#

Body arguments:

{
    "lastLoginTime": "#UTC timestamp of format YYYYMMDDTHHMMSSZ#"
}


Mandatory arguments: lastLoginTime
Example queries:
PUT https://eduid.ch/api/v1/mail/john.doe%40example.org
PUT https://eduid.ch/api/v1/swissEduID/12345678-b62c-44f5-87ff-f8d828a92334

{
    "lastLoginTime": "20161215T145649Z"
}

Description: Can be used to update a particular field of the user with the given email address or edu-ID identifier.
Response: If the operation is successful, the HTTP response code 200 is returned with an empty JSON array.

{}

 

Create an affiliation for an edu-ID account via pull method

The purpose of this method is to allow an organization to start the creation of a current affiliation for a given edu-ID user identified by his edu-ID identifier value. Issuing a request does not set the affiliation directly but instead triggers an attribute query (by edu-ID Attribute Aggregator) to the specified attribute authority. Only if that query is successful (= personal user attributes are returned), a new affiliation gets set.

This method is to be used in the attribute pull scenario. To create an affiliation in the attribute push scenario please use the Affiliation API

Authentication

Requests are authenticated with HTTP Basic Authentication. The logic should ensure that affiliations generally can only be triggered by an organisation for their own attribute authority. Some requests (e.g. from SWITCH) should also be allowed to trigger attribute queries for third party attribute authorities for administrative purposes.

General API request structure

In line with the existing API requests in the API Specification the proposal is to use the following structure for the request:

PUT /api/#version#/#objectType#/#object#/#subObjectType#

with the parameters:

PUT /api/#version#/swissEduID/#swissEduID#/affiliations
{
    "entityID":"$entityID",
    "swissEduPersonUniqueID":"$swissEduPersonUniqueID",
    "validFrom":"$timestamp" 
}

Parameters and Arguments:

  • #version#, mandatory:
    Version of the API, currently "v1"
  • #swissEduID#, mandatory:
    edu-ID identifier value for which to set the affilation
  • #swissEduPersonUniqueID#, mandatory:
    UniqueID identifier of affiliation to set. This is needed because users can have multiple affiliations at an organisation (e.g. one as student, one as staff member)
  • entityID, mandatory:
    Attribute Authorities's entityID to query.
    It must correspond to the value in the SWITCHaai federation metadata.
  • validFrom, optional:
    Date-time value (according to https://www.ietf.org/rfc/rfc3339.txt) after which affiliation
    should be set. If provided, an asynchronous attribute query is initiated, otherwise a synchronous attribute query will be scheduled.

Request description

There are two types of requests, synchronous and asynchronous.

Synchronous requests

Sending synchronous requests (without the validFrom parameter) immediately results in an attribute query to be sent to the Attribute Authority of the given entity specified using using swissEduID as NameIdentifier.

Asynchronous requests

Asynchronous requests contain a validFrom parameter whose date/time is in the future. The attribute query then is executed the same minute as the date/time provided indicates.
The attribute query is currently a SAML Attribute Query (but later may also be a direct LDAP query to a migrated organisation's LDAP server).

Example request

PUT /api/v1/swissEduID/8d3a49fe-b62c-44f5-87ff-f8d828a9375b/affiliations
{
    "entityID":"https://aai-logon.ethz.ch/idp/shibboleth",
		"swissEduPersonUniqueID":"23wecu324809u@ethz.ch",
		"validFrom":"2018-06-26T14:01:21Z" 
}

Response

  • HTTP Code 200 OK and {}:
    If (in a synchronous) user account was found and request was triggered but no new affiliation was set
  • HTTP Code 201 Created and {}:
    If (in a synchronous) user account was found and request was triggered and a new current affiliation was created for the user
  • HTTP Code 202 Accepted and {}:
    If (in an asynchronous request with validFrom) user account was found and request was stored in message queue for later execution
  • HTTP Code 401 Unauthorized:
    If authentication information is missing
  • HTTP Code 403 Forbidden:
    If user is not allowed to send this type of request or if he is not allowed to trigger this affiliation query
  • HTTP Code 404 Not Found:
    If user account was not found.
  • HTTP Code 500 Internal Error:
    The server encountered an unexpected condition which prevented it from fulfilling the request (e.g. validFrom in past)

How to make requests

Making requests to the edu-ID API is generally very simple and all that is needed is a web client. Therefore, one can use for example curl, which is available on all operating systems. The general curl command to set an affiliation then would look like:

curl -s -basic -u "${apiusername}:${apipassword}" -X PUT -H "Content-Type: application/json" -d "{\"entityID\":\"${entityID}\",\"validFrom\":\"${timestamp}\"}" "${apiurl}${swissEduID}" 

An example request to set an affilation now would look like:

curl -s -basic -u "ethz:1234abcde" -X PUT -H "Content-Type: application/json" -d '{"entityID":"https://aai-logon.ethz.ch/idp/shibboleth", "swissEduPersonUniqueID":"23wecu324809u@ethz.ch"}' "https://eduid.ch/api/v1/swissEduID/8d3a49fe-b62c-44f5-87ff-f8d828a9375b/affiliations" 

 

An example request to set an affilation in the future could therefore look like:

curl -s -basic -u "ethz:1234abcde" -X PUT -H "Content-Type: application/json" -d "{\"entityID\":\"https://aai-logon.ethz.ch/idp/shibboleth\", \"swissEduPersonUniqueID\":\"23wecu324809u@ethz.ch\" ,\"validFrom\":\"2018-06-26T14:01:21Z\"}" "https://eduid.ch/api/v1/swissEduID/8d3a49fe-b62c-44f5-87ff-f8d828a9375b/affiliations" 

 

Check with bulk request if edu-ID accounts still exist

POST /api/#version#/bulk

Example query: POST https://eduid.ch/api/v1/bulk
Description: This bulk API request can be used to check if a number of edu-ID accounts still exist, if they were deleted or if they were merged. In the latter case, the identifiers of the remaining merged account are returned as well as the users current name and email, even if an account was merged several times.
Request Content: The request body must be of the following format:

{
    "action": "check-account-status",
    "list": [
            { "swisseduid": "#swissEduID identifier#" },
             { "swisseduid": "#swissEduID identifier#" },
             { "swisseduid": "#swissEduID identifier#" },
             { "swisseduid": "#swissEduID identifier#" },
            (...)
        ]
}
Instead of the edu-ID identifier, alternatively, also the unique ID can be used for this type of query:
{
    "action": "check-account-status",
    "list": [
            { "swissedupersonuniqueid": "#swissEduPersonUniqueID identifier#" },
             { "swissedupersonuniqueid": "#swissEduPersonUniqueID identifier#" },
             { "swissedupersonuniqueid": "#swissEduPersonUniqueID identifier#" },
             { "swissedupersonuniqueid": "#swissEduPersonUniqueID identifier#" },
            (...)
        ]
}
 
An example request body therefore could look like this:
{
    "action": "check-account-status",
    "list": [
            { "swisseduid": "1234ae9c-deeb-46db-914b-8f99213b831a" },
             { "swisseduid": "00000000-0000-0000-00000000000000000" },
             { "swissedupersonuniqueid": "234234@eduid.ch" },
             { "swisseduid": "2389ae9c-deeb-46db-914b-8f99213b831a" }
        ]
}

 
Response: The HTTP response code of a request generally 200. Individual status codes are returned for each queried edu-ID identifier records within the JSON object:

{ 
   "action": "check-account-status",
   "results": #number of results#,
   "list":  [
            { "swisseduid": #,
"status": #status code#,
"new-swisseduid": "#new swissEduID identifier#",
"new-swissedupersonuniqueid": "#new uniqueID identifier#"
},
"mail": "#primary e-mail address of user#"},
"givenname": "#first name of user#"},
"sn": "#last name of user#"
}
}

        ]
}

The status codes have the following meaning:

  • 200 OK: Account still exists
  • 301 Moved Permanently: The account with the given edu-ID identifier was merged. The identifiers of the remaining merged account are returned.
  • 404 Not Found: The account with the given edu-ID identifier does not exist and has never existed.
  • 410 Gone: The account with the given edu-ID identifier was deleted

Thus an example response would look like:

{ 
   "action": "check-account-status",
   "results": 4,
   "list":  [
            { "swisseduid": "1234ae9c-deeb-46db-914b-8f99213b831a", "status": 200 },
            { "swisseduid": "00000000-0000-0000-00000000000000000", "status": 404 },
            { "swissedupersonuniqueid": "5234234@eduid.ch", "status": 410 },
            { "swisseduid": "2389ae9c-deeb-46db-914b-8f99213b831a", "status": 301,
"new-swisseduid": "789ae237-c664-4ed2-8e83-2c38e05186e7"
,
"new-swissedupersonuniqueid": "591634407021@eduid.ch"
},
"mail": "john.doe@example.org"},
"givenname": "John"},
"sn": "Doe"
}
}

        ]
}