Info e-teenuse arendajatele (In English)
HarID uses OpenID Connect with OAuth 2.0 protocol (YouTube short intro) for authentication and authorization. For security implications of getting the implementation correct, we require you to use OAuth 2.0 libraries when interacting with HarID endpoints. Best practice would be to use well written code provided by others - it will help you protect yourself and your users.
E-service registration
Before you can start sending OpenID/OAuth requests, you have to register your e-service.
Please contact with HarID support (harid@tugi.edu.ee) and provide following information:
1. Your organization name and registration number.
2. Your E-service return urls where HarID forwards back (you can add multiple urls if needed).
After that we can add you to our test-environment (test.harid.ee) and provide you with our OpenID client credentials.
Quick technical configuration parameters in JSON format
Test environment (GET request): https://test.harid.ee/.well-known/openid-configuration
Live environment (GET request): https://harid.ee/.well-known/openid-configuration
OpenID/OAuth technical info
Technical configuration parameters in JSON format:
You can get the latest info for test environment at (GET request) : https://test.harid.ee/.well-known/openid-configuration
You can get the latest info for live environment at (GET request): https://harid.ee/.well-known/openid-configuration
{
"issuer": "https://harid.ee",
"authorization_endpoint": "https://harid.ee/et/authorizations/new",
"jwks_uri": "https://harid.ee/jwks.json",
"response_types_supported": [
"code", "token", "id_token", "code token", "code id_token", "id_token token", "code id_token token"
],
"subject_types_supported": [ "public" ],
"id_token_signing_alg_values_supported": [ "RS256" ],
"token_endpoint": "https://harid.ee/et/access_tokens",
"userinfo_endpoint": "https://harid.ee/et/user_info",
"registration_endpoint": "https://harid.ee/et/connect/client",
"scopes_supported": [
"personal_code", "phone", "email", "profile", "openid", "roles", "session_type"
], "grant_types_supported": [ "authorization_code", "implicit" ],
"request_object_signing_alg_values_supported": [ "HS256", "HS384", "HS512" ],
"token_endpoint_auth_methods_supported": [ "client_secret_basic", "client_secret_post" ],
"claims_supported": [ "sub", "iss", "name", "email", "phone_number", "personal_code", "roles" ]
}
Data attributes
Exchange code for access token and ID token:
access_token |
A token that can be sent to HarID. |
id_token |
Identity information (JWT) about the user that is digitally signed by HarID. |
expires_in |
The remaining lifetime of the access token. |
token_type |
Identifies the type of token returned. At this time, this field always has the value 'Bearer'. |
Obtain user information from the ID token:
Example response in json format:
{ "sub": "5727bdad-6f71-4b99-8730-6d66170afa41", "name": "Example User", "given_name": "Example", "family_name": "User", "email": "user@example.com", "email_verified": true, "personal_code": "EE:EID:50701124378", "personal_code_verified": true, "strong_session": false, "roles": [ { "marker": "student", "active": true, "start_date": "2018-01-17", "end_date": null, "name_et": "Õpilane", "name_en": "Student", "name_ru": "student", "desc_et": null, "desc_en": null, "desc_ru": null, "provider_ehis_id": "293", "provider_reg_nr": "75014445", "provider_name": "Keila ühisgümnaasium", "created_at": "2018-01-17T16:25:50+02:00", "updated_at": "2018-01-17T16:27:55+02:00", "student_grade": "6", "student_parallel": "A" }, { "marker": "faculty", "active": null, "start_date": "2018-10-09", "end_date": null, "name_et": "faculty", "name_en": "Faculty", "name_ru": "faculty", "desc_et": null, "desc_en": null, "desc_ru": null, "provider_ehis_id": "223", "provider_reg_nr": "75012452", "provider_name": "Ääsmäe põhikool", "created_at": "2018-10-09T13:44:13+03:00", "updated_at": "2018-10-09T13:44:13+03:00" }, { "marker": "library-walk-in", "active": null, "start_date": "2018-10-09", "end_date": null, "name_et": "library-walk-in", "name_en": "Library-walk-in", "name_ru": "library-walk-in", "desc_et": null, "desc_en": null, "desc_ru": null, "provider_ehis_id": null, "provider_reg_nr": "75012452", "provider_name": "Ääsmäe põhikool", "created_at": "2018-10-09T13:44:13+03:00", "updated_at": "2018-10-09T13:44:13+03:00" }, { "marker": "student", "active": true, "start_date": "2018-12-13", "end_date": null, "name_et": "student", "name_en": "Student", "name_ru": "student", "desc_et": null, "desc_en": null, "desc_ru": null, "provider_ehis_id": "214", "provider_reg_nr": "75004429", "provider_name": "Albu põhikool", "created_at": "2018-12-13T14:42:40+02:00", "updated_at": "2018-12-14T19:13:54+02:00", "student_grade": "5", "student_parallel": null } ], "ui_locales": "et", "custodies": [ { "name": "Hernes Hernes", "given_name": "Hernes", "family_name": "Hernes", "email": "priit.tark+020@gmail.com", "email_verified": true, "personal_code": "EE:EID:60005050011", "personal_code_verified": false, "roles": [ { "marker": "student", "active": true, "start_date": "2018-12-13", "end_date": null, "name_et": "Õpilane", "name_en": "Student", "name_ru": "student", "desc_et": null, "desc_en": null, "desc_ru": null, "provider_ehis_id": "214", "provider_reg_nr": "75004429", "provider_name": "Albu põhikool", "created_at": "2018-12-13T14:42:40+02:00", "updated_at": "2018-12-14T19:13:54+02:00", "student_grade": "5", "student_parallel": null } ] }, { "name": "Loos Loos", "given_name": "Loos", "family_name": "Loos", "email": "priit.tark+011@gmail.com", "email_verified": true, "personal_code": "EE:EID:38612232328", "personal_code_verified": false, "roles": [ ] } ] }
}
NB! If client does not require scope, HarID does not return scope attributes.
All attributes:
Scope | Attribute | Description |
openid |
sub |
Required scope, always include it. An identifier for the user, unique for HarID accounts and never reused. HarID account can have multiple emails at different points in time, but the sub value is never changed. Use sub within your e-service as the unique-identifier key for the user. Example: {"sub": "5727bdad-6f71-4b99-8730-6d66170afa41"} |
profile |
name |
Always present, user's full name. User's full name in displayable form including all name parts: given name, middle name, family name. When visitor_account is true, then name is unverified and visitor can alter name without strong verification. |
profile | given_name | Always present, user first name. Given name(s) or first name(s) of the User. Note that in some cultures, people can have multiple given names; all can be present, with the names being separated by space characters. When visitor_account is true, then given_name is unverified and visitor can alter given_name without strong verification. |
profile | middle_name | Optional, user middle name. Middle name(s) of the User. Note that in some cultures, people can have multiple middle names; all can be present, with the names being separated by space characters. Also note that in some cultures, middle names are not used. When visitor_account is true, then middle_name is unverified and visitor can alter middle_name without strong verification. (not implemented, will come with the visitor support) |
profile | family_name | Always present, user last name. Surname(s) or last name(s) of the User. Note that in some cultures, people can have multiple family names or no family name; all can be present, with the names being separated by space characters. When visitor_account is true, then family_name is unverified and visitor can alter family_name without strong verification. |
profile | gender | Optional. User's gender. Values defined by this specification are female, male or empty. When visitor_account is true, user can alter this attribute without strong verification. (not implemented, will come with the visitor support) |
profile | birthdate | Optional, User's birthday, represented as an ISO 8601:2004 [ISO8601‑2004] YYYY-MM-DD format. When visitor_account is true, then birthdate is unverified and visitor can alter birthdate without strong verification. (not implemented, will come with the visitor support) |
profile | updated_at | Always present, time the End-User's information was last updated. The time is represented as the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the date/time. (not yet implemented, will come with the visitor support) |
profile | ui_locales | Indicates user preferred user interface locales, example: 'et' for Estonia or 'et en' where Estonia would be preferred over English. |
|
The user's email address. This may not be unique and is not suitable for use as a primary key. Provided only if your scope included the string "email". |
|
email_verified |
True if the user's e-mail address has been verified; otherwise false. |
|
personal_code |
personal_code |
Mostly Estonia's identity code. Always have prefix with 2 colons. Example EE:EID:11412090004 User cannot alter personal code. Personal code are stored only through strong verification. When visitor_account is true, then personal_code is empty. |
personal_code | personal_code_verified | When visitor_account is true, then personal_code is empty and personal_code_verified is false. |
session_type | strong_session |
True if user user last authentication into HarID was done by using strong authentication method such as Mobile-ID or ID-card. You can use standard way by ID token where "arm" value is either "pwd" (password auth) or "pop" (mobiil-ID/ID-card auth). |
phone | phone_number | User phone number in format "+372 12345678" where phone starts with calling code with "+" sign. |
phone | phone_number_verified | True if user has created an account using strong Mobile-ID authentication. User can alter non Mobile-ID account phone number. |
roles | roles | User roles, user can have multiple roles |
roles |
roles:marker | Unique identifier of user role.
'director' => Provider/school director 'faculty' => Provider/school teacher 'student' => Provider/school student 'secretary' => Provider/school secretary 'sysadmin' => Provider/school sysadmin Three roles are assigned automatically based on EHIS database with following map: EHIS role 'õpilane' => HarID role 'student' EHIS role 'õpetaja' => HarID role 'faculty' EHIS role 'direktor' => HarID role 'director' Provider sysadmin/secretary can manage user roles directly in HarID portal without EHIS change. |
roles |
roles:active | Defines, if role is valid. Only roles with value "true" are valid. Please always check active status because HarID sends also inactive roles. Keep in mind that roles can become inactive temporarily. |
roles |
roles:start_date | Role start date. Start date does not define alone, if role is active. Role must be also active with value "true" and start date must be in past to determine you can rely on it. |
roles |
roles:end_date | Role end date. End date does not define alone, if role is active. Role must be also active with value "true" and end date must missing or in the future. |
roles |
roles:name_et | Role name in Estonian |
roles |
roles:name_en | Role name in English |
roles |
roles:name_ru | Role name in Russian |
roles |
roles:desc_et | Role description in Estonian |
roles |
roles:desc_en | Role description in English |
roles |
roles:desc_ru | Role description in Russian |
roles |
roles:provider_reg_nr | Provider's (institution (school)) registration number |
roles |
roles:provider_name | Provider (example school) name |
roles |
roles:created_at | Role creation time, format: ISO8601 |
roles |
roles:updated_at | Role updated time, format: ISO8601 |
roles |
roles:student_grade | Only present when user has 'student' role, provides grade, example value: '6' |
roles |
roles:student_parallel |
Only present when user has 'student' role, provides class parallel info, example: 'A' |
custodies | name | String, full name |
custodies | given_name | First name |
custodies | family_name | Family name |
custodies | ||
custodies | email_verified | Boolean, true or false, Child has verified his/her email |
custodies | personal_code | Child personal code |
custodies | roles | Same attributes as main role attributes. |
NB! If client does not require scope, HarID does not return scope attributes.
NB! During development, when you change scopes, we recommend to log in to HarID and manually remove all previous confirms.
Visitor account support
HarID team is implementing a new visitor's account support, where school's sysadmin or secretary can invite visitors by simple email invitation. Visitor support is made for foreigners, who don't have any strong authentication using Estonia ID/Mobile-ID/SmartID or TARA. Visitor account is similar how currently parent is inviting own child by email with only difference that visitor has empty personal code attribute.
Keep in mind, that visitor can become regular user after visitor start using TARA or having Estonia ID card. Then visitor_account will return false and personal code is present. However many foreign countries do not have personal code, therefor without personal code, those account will be visitors forever.
In short:
1) Please add a scope "visitor" to your authentication request in order to declare your e-service is supporting visitor's account. When you don't use scope "visitor", then HarID portal will inform visitor that your e-service is not supporting visitor's account and HarID portal does not redirect visitor to your e-service.
2) Visitor's account "personal_code" is empty and you cannot use personal code to bind users. You should use "sub" value for binding HarID visitors with your e-service accounts. Sub value is an identifier for the user, unique for HarID accounts and never reused. Use sub within your e-service as the unique-identifier key for the user. Sub value is present for all HarID users. Example: {"sub": "5727bdad-6f71-4b99-8730-6d66170afa41"}
3) Visitor's absolute minimum profile always has: sub, email, given name and family name. All other attributes might not be present.
Visitor scope attributes:
Scope | Attribute | Description |
---|---|---|
visitor | visitor_account | Always present. true if personal_code is blank, otherwise false. Keep in mind, that visitor can turn into user after visitor has bindid account with any strong verification what provides personal_code. |
visitor | country | Optional. User unverified primary country of origin in ISO 3166-1 format. Brazil example: "BR" |
NB! Keep in mind that visitor account's given name, middle name, family name, gender, birthdate are user changable! As soon visitor converts visitor account to verified account by strong authentication, user is not able to change those attributes any more. User account cannot convert to vistior's account. Therefore be careful in systems where users's name is important because HarID does not verify visitors name and only users account names are verified.
OpenID/OAuth technical error description
Error name |
Error description and debug info |
insufficient_scope |
Insufficient scope error are returned from user_info request. It means e-service request are denied. Any following situation might cause it: 1) E-service has not requested any scopes, thus HarID does not return any info. 2) E-service has not requested mandatory 'openid' scope. Double check your system has requested at least 'openid' scope. 3) E-service has done everything right but end user has denied giving info out. Please login into HarID portal and remove all E-service authorizations because during testing/development you might previously have denied authorization thus HarID refuse to return requested info. It the future, when your e-service change scopes, then all your users current authorizations will be canceled automatically and all your users must authorize your e-service new scopes. |
OpenID/OAuth technical low-level flow for debugging
You can test/play with a demo client at https://kool.domify.io
HarID service only supports official OAuth 2.0 libraries We provide low-level flow in order to better understand and debug OpenID Connect protocol.
Terms:
- client - Demo Pihlaka school at https://kool.domify.io
- HarID - Demo HarID server at https://test.harid.ee
1. User visits client website and clicks HarID button. Client system will redirect user to HarID system. Please follow protocol defined at RFC6749 4.1.1 Authorization Request. Include all requested scopes (dark text):
GET redirect request:
https://test.harid.ee/et/authorizations/new?client_id=a6095bd2e837af2ef8c69ee87d7028d8&nonce=0d790445152f8b8bca5a847170e987b5&redirect_uri=https%3A%2F%2Fkool.domify.io%2Fproviders%2F2%2Fopen_id&response_type=code&scope=openid+profile+email+personal_code+roles+session_type+custodies&state=0d790445152f8b8bca5a847170e987b5
NB! The nonce you provide is required to verify by your system at later steps. More info at the OpenID connect core spec and a security explanation.
2. User logs in to HarID and grant permissions for the authorization request. HarID redirect user back to client following redirect url (redirect url must register before at HarID support).
3. Client system makes GET request "https://test.harid.ee/.well-known/openid-configuration" and gets where to make Access Token request. Client makes access_token request.
POST /et/access_tokens HTTP/1.1
Authorization: Basic MWViNTJjNDc0MmUyNzBkY2ZiNzA5NjYzNjVhNzUwOWM6MWQ0ODZlODk0NjVmZmM0MmIxZDliNTc0OTU4MThlNzJhMmExYTgxNjNhNWM1ZDFhMDY1NTc4ZGQyYjY0MmMyZA==
Content-Type: application/x-www-form-urlencoded
Content-Length: 167
Host: test.harid.ee
grant_type=authorization_code&code=7677a231beefcc80465c71d15766caa3ff8c5fc3789f45e7c03569fdf27436cc&redirect_uri=https%3A%2F%2Fkool.domify.io%2Fproviders%2F1%2Fopen_id
HarID returns access_token:
GET /et/user_info? HTTP/1.1
Authorization: Bearer 981574fe1aeba078a767329e7fcd354da48ee2bfe4440a365b534690c5b9f494
Host: test.harid.ee
Status: 200 OK
Content-Type: application/json
Connection: keep-alive
Status: 200 OK
Cache-Control: no-store
Strict-Transport-Security: max-age=31536000
Pragma: no-cache
X-Request-Id: 46f4bcca-05f5-4b49-9197-4522f4365f58
ETag: W/"28b332d4c5e80bf8641b4710aa339e6a"
X-Runtime: 0.071670
Date: Tue, 14 Apr 2020 08:10:10 GMT
X-Powered-By: Phusion Passenger 6.0.4
Server: nginx/1.14.2 + Phusion Passenger 6.0.4
Content-Length: 0
{"access_token":"981574fe1aeba078a767329e7fcd354da48ee2bfe4440a365b534690c5b9f494","token_type":"bearer","expires_in":86399,"id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImRlZmF1bHQifQ.eyJpc3MiOiJodHRwczovL2hhcmlkLmdpdGxhYi5ldSIsInN1YiI6ImU4NmVmNTkyLTkyNjItNDg5Zi05YmQxLTE0OTA5ZDE3MmQ4OSIsImF1ZCI6IjFlYjUyYzQ3NDJlMjcwZGNmYjcwOTY2MzY1YTc1MDljIiwiZXhwIjoxNTg2ODUyMTEwLCJpYXQiOjE1ODY4NTE4MTAsIm5vbmNlIjoiOTYwOTE5YmQyMDVhMDU0ODAyYzZkODhlOGRmYjk5YTkiLCJhbXIiOiJbXCJwd2RcIl0ifQ.MsQ7xdNMBw2uU1IeikvZ5AVq00UVl7JInKhRGeGc5vwcogGSkFL3293zT1KGveDSJbSZG9IwffuMqZX_OLhbh-xLExSkvOHe3CdOlCQHq3O0bdxlCgZVelzzOaXSO5274Uj-SMsA-cCqKYqoh5v2SH5ZcG5-I6XwMJV_wsmvUJccvv-vkC_ConI8rTjUi2S0rGBK0tm-AIksY0T6GOA0odd0lo_XmOrY60GjXt-t2PDvg6dFytiJOPykjtlQYywsanBgqvzk2HWkcY51DMKd3930urirehwOW-HEfVERB1cD3TBktVpxVXICLzHZHANe3MwJJYw8n5huMBcK4PX6zA"}
4. Client makes now user_info request with access_token as follows:
GET /et/user_info? HTTP/1.1
Authorization: Bearer 981574fe1aeba078a767329e7fcd354da48ee2bfe4440a365b534690c5b9f494
Host: test.harid.ee
HarID returns:
Status: 200 OK
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Status: 200 OK
Cache-Control: max-age=0, private, must-revalidate
Strict-Transport-Security: max-age=31536000
X-XSS-Protection: 1; mode=block
X-Request-Id: 9088d5ac-755f-4079-8f7f-b349ae4eedf9
ETag: W/"676467cd54d2594efb6ef312f05a7ada"
X-Frame-Options: SAMEORIGIN
X-Runtime: 0.033594
X-Content-Type-Options: nosniff
Date: Tue, 14 Apr 2020 08:10:10 GMT
X-Powered-By: Phusion Passenger 6.0.4
Server: nginx/1.14.2 + Phusion Passenger 6.0.4
Content-Length: 0
{"sub":"e86ef592-9262-489f-9bd1-14909d172d89","name":"Priit Test","given_name":"Priit","family_name":"Test","email":"priit@example.com","email_verified":true,"personal_code":"EE:EID:34501234215","strong_session":false,"roles":[{"marker":"sysadmin","active":true,"start_date":"2019-08-12","end_date":null,"name_et":"sysadmin","name_en":"sysadmin","name_ru":"sysadmin","desc_et":null,"desc_en":null,"desc_ru":null,"provider_ehis_id":null,"provider_reg_nr":"75002426","provider_name":"Aakre lasteaed-algkool","created_at":"2019-08-12T15:24:49+03:00","updated_at":"2019-08-12T15:25:27+03:00","student_grade":null,"student_parallel":null},{"marker":"secretary","active":true,"start_date":"2020-01-06","end_date":null,"name_et":"secretary","name_en":"secretary","name_ru":"secretary","desc_et":null,"desc_en":null,"desc_ru":null,"provider_ehis_id":null,"provider_reg_nr":"123","provider_name":"Rapla Gümnaasium","created_at":"2020-01-06T11:17:51+02:00","updated_at":"2020-01-06T11:18:16+02:00","student_g
rade":null,"student_parallel":null},{"marker":"student","active":null,"start_date":"2020-02-12","end_date":null,"name_et":"student","name_en":"Student","name_ru":"student","desc_et":null,"desc_en":null,"desc_ru":null,"provider_ehis_id":null,"provider_reg_nr":"75002426","provider_name":"Aakre lasteaed-algkool","created_at":"2020-02-12T16:12:51+02:00","updated_at":"2020-02-12T16:12:51+02:00","student_grade":null,"student_parallel":null},{"marker":"student","active":null,"start_date":"2020-02-12","end_date":null,"name_et":"student","name_en":"Student","name_ru":"student","desc_et":null,"desc_en":null,"desc_ru":null,"provider_ehis_id":null,"provider_reg_nr":"75002426","provider_name":"Aakre lasteaed-algkool","created_at":"2020-02-12T16:13:20+02:00","updated_at":"2020-02-12T16:13:20+02:00","student_grade":null,"student_parallel":null}],"ui_locales":"et","custodies":[]}