De relevante gebruikers en hun groepslidmaatschappen worden via het SCIM protocol doorgegeven aan de endpoints bij instellingen. De invite-applicatie heeft de rol van SCIM client om informatie aan de verschillende Service Providers te sturen.
| Begrip | Omschrijving |
|---|---|
| Service Provider | Een applicatie bij de instelling, waar een gast-gebruiker toegang moet krijgen |
| SCIM client | De applicatie die de gebruikersinformatie naar de Service Providers stuurt; De Invite-applicatie backend |
De SCIM-endpoints kunnen per instelling op verschillende manieren worden beveiligd. De invite-applicatie ondersteunt op dit moment de volgende authenticatiemethoden:
HTTP Basic Authentication
De client stuurt een Authorization: Basic ... header met gebruikersnaam en
wachtwoord.
Bearer token authenticatie
De client stuurt een Authorization: Bearer ... header met een vooraf
verstrekt token.
Stuur de gegevens voor het SCIM endpoint naar support@surfconext.nl. Vermeld in ieder geval:
De endpoints bij de instellingen ondersteunen de volgende operaties:
https://example.com/{v}/{resource}https://example.com/{v}/{resource}/{id}https://example.com/{v}/{resource}/{id}https://example.com/{v}/{resource}/{id}https://example.com/{v}/{resource}/{id}https://example.com/{v}/{resource}?filter={attribute}{op}{value}&sortBy={attributeName}&sortOrder={ascending|descending}De invite-applicatie roept alleen Create, Replace, Update en Delete aan; Read en Search worden niet gebruikt.
PUT operaties leveren het complete object; PATCH operaties geven het verschil met het huidige object door. Zie rfc7644 section-3.5.2
Er zijn meerdere attributen die een gebruiker of groep identificeren:
Voor de gebruikers die via de invite-applicatie beheerd worden, worden de
userName en externalId gevuld met een attribuut van de gebruiker, zodat ze
ook bij een SAML of oidc authenticatie herkend kunnen worden. Per endpoint kan
door SURFconext support met scim_user_identifier worden ingesteld welk attribuut gebruikt
wordt:
scim_user_identifier |
Attribuut |
|---|---|
eduperson_principal_name (standaard) |
eduPersonPrincipalName (eppn) |
subject_id |
subject_id (OIDC-claim) |
uids |
uids (SAML-attribuut) |
email |
e-mailadres van de gebruiker |
eduID |
het (institutionele) eduID-pseudoniem |
Als het gekozen attribuut leeg is, valt de invite-applicatie terug op de
eduPersonPrincipalName (eppn). Bij scim_user_identifier = eduID
provisioneert de invite-applicatie eerst het eduID van de gebruiker bij de
instelling en gebruikt de teruggekregen institutionele eduID-waarde als
userName en externalId. Voor gastgebruik met eduID heeft
de eduID identifier
de voorkeur.
Na het accepteren van de eerste uitnodiging van een instelling, moet de gebruiker aangemaakt worden bij de instelling.
POST /v1/Users HTTP/1.1
Accept: application/json
Authorization: Basic dXNlcjpwYXNzd29yZA==
Host: example.com
Content-Length: ...
Content-Type: application/json
{
"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"],
"externalId":"c2cd7d6e-63fc-493a-8746-62fb2d3f8806",
"userName":"c2cd7d6e-63fc-493a-8746-62fb2d3f8806",
"name":{
"formatted":"Peter Havekes",
"familyName":"Havekes",
"givenName":"Peter"
},
"displayName": "Peter Havekes",
"active": true,
"emails":[
{
"type":"other",
"value":"peter@gmail.com"
}
],
"phoneNumbers":[
{
"type":"other",
"value":"+31600000000"
}
]
}
Het phoneNumbers-attribuut bevat altijd een dummy-nummer
(+31600000000), omdat sommige systemen dit attribuut verplicht stellen.
HTTP/1.1 201 Created
Content-Type: application/scim+json
Location: https://example.com/v1/Users/{UserID at SP}
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
],
"displayName": "Peter Havekes",
"meta": {
"created": "2021-12-22T12:34:56Z",
"location": "https://example.com/v1/Users/{UserID at SP}",
"lastModified": "2021-12-22T12:34:56Z",
"resourceType": "User"
},
"name":{
"familyName":"Havekes",
"givenName":"Peter"
},
"id": "{UserID at SP}",
"userName":"c2cd7d6e-63fc-493a-8746-62fb2d3f8806",
"emails":[
{
"type":"other",
"value":"peter@gmail.com"
}
]
}
De {UserID at SP} uit het antwoord wordt in de Invite-applicatie opgeslagen bij de user, voor toekomstige updates van de gebruiker.
Als bij het aanmaken van de uitnodiging via de API
gebruik is gemaakt van invitesWithInternalPlaceholderIdentifiers, dan zal de
waarde van internalPlaceholderIdentifier worden doorgegeven als
"externalId": "{internalPlaceholderIdentifier}" bij het POST bericht om een
gebruiker aan te maken.
Als de gegevens van een gebruiker veranderd zijn (veranderde attributen tijdens de authenticatie), dan sturen we een geupdate user-object naar alle service providers waar deze gebruiker bekend is.
PUT /v1/Users/{UserID at SP} HTTP/1.1
Accept: application/json
Authorization: Basic dXNlcjpwYXNzd29yZA==
Host: example.com
Content-Length: ...
Content-Type: application/json
{
"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"],
"externalId":"c2cd7d6e-63fc-493a-8746-62fb2d3f8806",
"userName":"c2cd7d6e-63fc-493a-8746-62fb2d3f8806",
"name":{
"formatted":"Peter Havekes-Nieuwenaam",
"familyName":"Havekes-Nieuwenaam",
"givenName":"Peter"
},
"id": "{UserID at SP}",
"displayName": "Peter Havekes-Nieuwenaam",
"active": true,
"emails":[
{
"type":"other",
"value":"peter@gmail.com"
}
],
"phoneNumbers":[
{
"type":"other",
"value":"+31600000000"
}
]
}
HTTP/1.1 200 OK
Content-Type: application/scim+json
Location: https://example.com/v1/Users/{UserID at SP}
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
],
"displayName": "Peter Havekes-Nieuwenaam",
"name":{
"familyName":"Havekes-Nieuwenaam",
"givenName":"Peter"
},
"meta": {
"created": "2021-12-22T12:34:56Z",
"location": "https://example.com/v1/Users/{UserID at SP}",
"lastModified": "2021-12-22T20:34:56Z",
"resourceType": "User"
},
"id": "{UserID at SP}",
"userName":"c2cd7d6e-63fc-493a-8746-62fb2d3f8806",
"emails":[
{
"type":"other",
"value":"peter@gmail.com"
}
]
}
Gebruikers worden na het verlopen van hun laatste rol bij een applicatie verwijderd uit SURFconext Invite, en ook bij alle service providers waar de gebruiker is aangemaakt. Eerst wordt de gebruiker uit alle groepen verwijderd; het gebruiker-object zelf wordt alleen verwijderd bij de service providers waar geen andere rollen meer van toepassing zijn.
DELETE /v1/Users/{UserID at SP} HTTP/1.1
Accept: application/json
Authorization: Basic dXNlcjpwYXNzd29yZA==
Host: example.com
Content-Length: ...
Content-Type: application/json
{
"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"],
"externalId":"c2cd7d6e-63fc-493a-8746-62fb2d3f8806",
"userName":"c2cd7d6e-63fc-493a-8746-62fb2d3f8806",
"name":{
"formatted":"Peter Havekes",
"familyName":"Havekes",
"givenName":"Peter"
},
"id": "{UserID at SP}",
"displayName": "Peter Havekes",
"active": true,
"emails":[
{
"type":"other",
"value":"peter@gmail.com"
}
],
"phoneNumbers":[
{
"type":"other",
"value":"+31600000000"
}
]
}
HTTP/1.1 200 OK
De rollen in de invite applicatie worden als groepen gepubliceerd naar de Service Provider.
Bij het aanmaken van een groep in de invite applicatie wordt deze direct verstuurd naar de instelling.
De externalId van een groep is de URN van de rol. Standaard wordt deze
opgebouwd uit het geconfigureerde URN-prefix, de identifier van de rol en de
naam van de rol ({prefix}:{identifier}:{rolnaam}). Voor rollen afkomstig
uit SURF Teams wordt de URN van de rol zelf gebruikt, en voor rollen uit het
CRM urn:mace:surfnet.nl:surfnet.nl:sab:role:{rolnaam}.
POST /v1/Groups HTTP/1.1
Accept: application/json
Authorization: Basic dXNlcjpwYXNzd29yZA==
Host: example.com
Content-Length: ...
Content-Type: application/json
{
"schemas":
[
"urn:ietf:params:scim:schemas:core:2.0:Group"
],
"externalId": "urn:collab:group:test.eduid.nl:wur.nl:brightspace:gastdocent",
"displayName":"WUR Brightspace gastdocent",
"members":
[
]
}
Bij het initieel aanmaken van een groep zal de lijst met members leeg zijn.
HTTP/1.1 201 Created
Content-Type: application/json
Location: https://example.com/v1/Groups/{GroupID at SP}
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:Group"
],
"displayName":"WUR Brightspace gastdocent",
"meta": {
"created": "2021-12-23T10:00:00Z",
"location": "https://example.com/v1/Groups/{GroupID at SP}",
"lastModified": "2021-12-23T10:00:00Z",
"resourceType": "Group"
},
"members":
[
],
"externalId": "urn:collab:group:test.eduid.nl:wur.nl:brightspace:gastdocent",
"id": "{GroupID at SP}"
}
De {GroupID at SP} uit het antwoord wordt in de Invite-applicatie opgeslagen bij de user, voor toekomstige updates van de groep.
Als een gebruiker een uitnodiging accepteert, wordt de gebruiker eerst aangemaakt (met bovenstaand user bericht) als deze nog niet bestond. Daarna wordt de gebruiker aan de bestaande groep toegevoegd door het hele groep-object (met alle leden) als update te sturen (PUT) of door het verschil door te geven (PATCH). Ook wanneer de naam van de rol is gewijzigd, wordt de groep bijgewerkt.
Per applicatie is in te stellen of groep-updates als PUT of PATCH verstuurd worden:
PUT operaties leveren het complete object; PATCH operaties geven het verschil met het huidige object door. Zie rfc7644 section-3.5.2
PUT /v1/Groups/{GroupID at SP} HTTP/1.1
Accept: application/json
Authorization: Basic dXNlcjpwYXNzd29yZA==
Host: example.com
Content-Length: ...
Content-Type: application/json
{
"schemas":
[
"urn:ietf:params:scim:schemas:core:2.0:Group"
],
"externalId": "urn:collab:group:test.eduid.nl:wur.nl:brightspace:gastdocent",
"id": "{GroupID at SP}",
"displayName":"WUR Brightspace gastdocent",
"members":
[
{
"value":"{UserID at SP}"
},
{
"value":"{Other UserID at SP}"
}
]
}
PATCH /v1/Groups/{GroupID at SP} HTTP/1.1
Accept: application/json
Authorization: Basic dXNlcjpwYXNzd29yZA==
Host: example.com
Content-Length: ...
Content-Type: application/json
{
"schemas" : [ "urn:ietf:params:scim:api:messages:2.0:PatchOp" ],
"Operations" : [ {
"op" : "add",
"path" : "members",
"value" : [ {
"value" : "{UserID at SP}"
} ]
} ]
}
PATCH /v1/Groups/{GroupID at SP} HTTP/1.1
Accept: application/json
Authorization: Basic dXNlcjpwYXNzd29yZA==
Host: example.com
Content-Length: ...
Content-Type: application/json
{
"schemas" : [ "urn:ietf:params:scim:api:messages:2.0:PatchOp" ],
"Operations" : [ {
"op" : "remove",
"path" : "members",
"value" : [ {
"value" : "{UserID at SP}"
} ]
} ]
}
Als de naam van de rol is gewijzigd, wordt de nieuwe naam doorgegeven met een
replace operatie op displayName:
PATCH /v1/Groups/{GroupID at SP} HTTP/1.1
Accept: application/json
Authorization: Basic dXNlcjpwYXNzd29yZA==
Host: example.com
Content-Length: ...
Content-Type: application/json
{
"schemas" : [ "urn:ietf:params:scim:api:messages:2.0:PatchOp" ],
"Operations" : [ {
"op" : "replace",
"path" : "displayName",
"value" : "Nieuwe rolnaam"
} ]
}
HTTP/1.1 200 OK
Content-Type: application/json
Location: https://example.com/v1/Groups/{GroupID at SP}
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:Group"
],
"displayName":"WUR Brightspace gastdocent",
"meta": {
"created": "2021-12-23T10:00:00Z",
"location": "https://example.com/v1/Groups/{GroupID at SP}",
"lastModified": "2021-12-23T14:01:00Z",
"resourceType": "Group"
},
"members":
[
{
"value":"{UserID at SP}"
},
{
"value":"{Other UserID at SP}"
}
],
"externalId": "urn:collab:group:test.eduid.nl:wur.nl:brightspace:gastdocent",
"id": "{GroupID at SP}"
}
Als groepen worden verwijderd vanuit de invite applicatie wordt dit ook doorgegeven aan de service provider.
DELETE /v1/Groups/{GroupID at SP} HTTP/1.1
Accept: application/json
Authorization: Basic dXNlcjpwYXNzd29yZA==
Host: example.com
Content-Length: ...
Content-Type: application/json
{
"schemas":
[
"urn:ietf:params:scim:schemas:core:2.0:Group"
],
"externalId": "urn:collab:group:test.eduid.nl:wur.nl:brightspace:gastdocent",
"id": "{GroupID at SP}",
"displayName":"WUR Brightspace gastdocent",
"members":
[
]
}
HTTP/1.1 200 OK