Okta: налаштування Grafana SSO з OIDC та Role mapping
0 (0)

Автор |  27/03/2026
Click to rate this post!
[Total: 0 Average: 0]

Нарешті на поточному проекті “доросли” до використання Okta, тому зараз буде невелика серія постів по ній.

Колись про Okta вже писав, але то було 5-6 років тому, і за цей час в ній є цікаві зміни (див. тег #okta).

Що будемо робити сьогодні – це налаштовувати SSO логін через Okta для Grafana.

Колись таке робив для ArgoCD та GitHub, але з SAML (див. What is: SAML – обзор, структура и трассировка запросов на примере Jenkins и Okta SAML SSO – 2019 рік, OMG…) – тепер зробимо для Grafana, але з OIDC.

При логіні з Okta в Grafana треба автоматично визначати яку Grafana Role йому видати – звичайного Viewer, або Admin, в залежності від того, яка у юзера група в Okta. Є два варіанти того, як це можна зробити – подивимось на обидва.

В Okta є готова App Grafana Labs – але вона підтримує тільки SAML, а хочеться модно-маладьожно, з OIDC – тому створимо окрему інтеграцію.

Документація Grafana – Configure Okta OIDC authentication, і навіть цілком робоча.

Єдина проблема, яка в мене виникла – це мапінг вже існуючих в Grafana юзерів із Google SSO з юзерами із Okta – трохи довелось покопатись.

Налаштування Okta

Що треба буде зробити в Okta – це створити нову App з OIDC, взяти її ключі для налаштувань самої Grafana, а потім налаштувати мапінг Okta Groups в Grafana Roles.

Створення Okta OIDC App for Grafana

Переходимо в Applications, створюємо нову апку, вибираємо метод логіну з OIDC, в Application type вказуємо Web Application:

Далі задаємо grant types:

  • Authorization Code: Grafana зможе виконувати логін юзера
  • Refresh Token: Grafana зможе оновлювати токен юзера без необхідності його релогіну

Див. Application Grant Types та OAuth 2.0 and OpenID Connect overview.

В URLs задаємо ті ендпоінти, що вказані в документації – https://<grafana_url>/login/okta та https://<grafana_url>/logout:

Controlled access можна налаштувати пізніше через Assign – або відразу вказати групи, яким буде підключена ця App:

Зберігаємо, і отримуємо ключі для Grafana:

В документації Grafana сказано, що тут ще мають бути і URLs – але їх нема. Втім, вони дефолтні, тому ОК.

Єдиний момент тут, це якщо використовується Okta Custom Domain – але про це вже поговоримо далі, в налаштуваннях самої Grafana.

Тут закінчили, тепер цікаве – мапінг груп Okta в Grafana Roles.

Configure Okta to Grafana role mapping

Тут є два варіанти: або створити кастомні Attributes для нової App, а потім їх задавати для Okta Groups – або в самій Grafana парсити значення в role_attribute_path.

В першому випадку трохи більше клікопсу, але більше гнучкості – а другий простіше, але можна поламати голову з написанням складних умов.

Потестив з обома варіантами – обидва працюють, почнемо з того, як це в документації Grafana – через кастомні атрибути.

Grafana Role на основі Okta App Profile та Custom Attributes

Ідея полягає в тому, що для Profile створеної App ми додаємо новий Attribute, в Grafana вказуємо поле, яке буде містити інформацію про Grafana Role, потім для Okta User або Okta Group задаємо власне значення цього атрибута – і воно передається до Grafana.

Далі, коли ми зробимо Assign цієї App до юзера – App візьме його дефолтні атрибути з Okta User Profile (firstName, lastName, email, login), додасть до них новий атрибут з Grafana Role – і це все разом передасть в Grafana, а далі вже справа самої Grafana – розпарсити поля і визначити Grafana Role цього юзера.

Див. The Okta User Profile and Application User Profile та Add custom attributes to apps, directories, and identity providers.

Переходимо до Directory > Profile Editor, знаходимо профайл для нової App:

Клікаємо Add Attribute:

Задаємо тип string, в Enum задаємо список ролей Grafana, в Attribute type можна вказати Group, аби менеджити ролі на рівні груп юзерів:

Далі в документації Grafana в частині Configure Groups claim говориться, що треба налаштувати передачу груп юзерів – але якщо ми передаємо роль через кастомний атрибут – то Grafana Role буде працювати і без цього.

А от якщо робити по другому варіанту – парсити групу і в залежності від групи видавати роль в Grafana – то це треба буде зробити, бо по дефолту групи юзера не передаються.

Отже, додали новий Attribute – повертаємось до списку Applications:

Виконуємо Refresh:

Переходимо до Okta Group, знаходимо підключену Grafana App:

Клікаємо Edit:

Задаємо значення атрибута Grafana Role:

Трохи забігаючи наперед – ось, що ми потім побачимо при логіні в Grafana logs – груп нема, але атрибут grafana_role="Admin" переданий:

...
logger=oauth.okta t=2026-03-27T13:58:14.843471331Z level=debug msg="Received user info response" raw_json="{\"sub\":\"***\",\"name\":\"Arseny Zinchenko\",\"locale\":\"en_US\",\"email\":\"[email protected]\",\"preferred_username\":\"[email protected]\",\"given_name\":\"Arseny\",\"family_name\":\"Zinchenko\",\"zoneinfo\":\"America/Los_Angeles\",\"updated_at\":1774610676,\"email_verified\":true,\"grafana_role\":\"Admin\"}" data="unsupported value type"

Grafana Role на основі Okta Group Name

Інший варіант – не морочитись з кастомними атрибутами, а передавати групи юзера із Okta до Grafana під час логіну, і потім в самій Grafana по імені групи визначати яку ролі підключити.

Для цього в Token claims треба додати передачу групи, і це, начебто, можна зробити через Add Expression і Okta Expression Language з, наприклад, Groups.startsWith():

Groups.startsWith("OKTA", "org-DevOps", 100)

Де “OKTA” – це source групи, а “org-DevOps” – фільтр, аби передавати групу тільки тоді, коли її ім’я починається з “org-DevOps“:

Але тоді Okta свариться, що “‘groups’ is reserved and cannot be used“:

Не став морочитись, і зробив через “Show legacy configuration”:

 

Тепер при логіні в Grafana ми отримаємо поле groups з усіма групами, до який належить юзер – це приклад з логу Grafana, де це вже налаштовано:

...
"groups\":[\""orgName-All-Users\",\""orgName-DevOps\",\""orgName-All-RnD\",\""orgName-Okta-Admins\"]}
...

Все – тепер можна налаштовувати саму Grafana.

Налаштування Okta Authentication в Grafana

Переходимо в Authentication, вибираємо Okta:

Задаємо ключі та URLs, про які говорили вище.

В моєму випадку використовується Okta Custom Domain, тому адреси будуть:

  • Auth URL: https://okta.example.com/oauth2/v1/authorize
  • Token URL: https://okta.example.com/oauth2/v1/token
  • API URL: https://okta.example.com/oauth2/v1/userinfo

Або використовуємо дефолтні з https://<TENANT_ID>.okta.com:

Тепер момент по ролям: якщо роль передаємо через App Profile та Custom Attribute – то в User mapping задаємо значення поля “Role attibute path” просто як grafana_role:

Тоді Grafana прочитає значення поля і замапить “Admin” від Okta в свою локальну роль “Admin”.

Якщо ж робимо через Okta Group Name, тобто передачу групи і без App Profile та кастомних атрибутів – то в Role attibute path пишемо JMESPath expression, в якому вказуємо:

contains(groups[*], 'orgName-DevOps') && 'Admin' || 'Viewer'

Тоді, якщо поле groups юзера містить ‘orgName-DevOps‘ – то йому буде видана Grafana Role “Admin”, якщо ні – то дефолтна роль “Viewer”.

Зберігаємо, відкриваємо логін в Grafana в іншому браузері чи інкогніто mode – і внизу маємо опцію логіна з Okta:

Клікаємо, нас редиректить на Okta:

Але перша спроба сфейлилась 🙂

Втім, якщо в Grafana такого юзера ще нема – то тут вже все має працювати без проблем:

Grafana OIDC та помилка “unable to create user: user not found”

Тут трохи довелось подебажити.

Включаємо debug logs – хоча на дебаг самої помилки не вплинуло, але цікаво було глянути що Grafana взагалі отримує від Okta:

...
    grafana.ini:
      log:
        level: debug
        filters: "oauth.okta:debug authn:debug"
...

Тепер бачимо всі поля від Okta:

...
logger=oauth.okta t=2026-03-27T11:36:21.745687386Z level=debug msg="Received user info response" raw_json="{\"sub\":\"***\",\"name\":\"Arseny Zinchenko\",\"locale\":\"en_US\",\"email\":\"[email protected]\",\"preferred_username\":\"[email protected]\",\"given_name\":\"Arseny\",\"family_name\":\"Zinchenko\",\"zoneinfo\":\"America/Los_Angeles\",\"updated_at\":1774610676,\"email_verified\":true,\"groups\":[\"orgName Users [old]\",\"Everyone\",\"orgName-All-Users\",\"orgName-DevOps\",\"orgName-All-RnD\",\"orgName-Okta-Admins\"],\"grafana_role\":\"Admin\"}" data="unsupported value type"
logger=user.sync t=2026-03-27T11:36:21.751172007Z level=error msg="Failed to create user" error="user not found" auth_module=oauth_okta auth_id=***
logger=authn.service t=2026-03-27T11:36:21.751234085Z level=error msg="Failed to run post auth hook" client=auth.client.okta id= error="[user.sync.internal] unable to create user: user not found"
...

Що мене тут напрягло – що Grafana намагається “unable to create user” і каже, що “user not found“.

Проблема в тому, що в нашій Grafana вже налаштований Google SSO, і я з ним колись логінився – а тому в Grafana вже є юзер з email":"[email protected]".

Аби Grafana змогла використовувати один і той самий email для різних identity providers – додаємо в grafana.ini опцію oauth_allow_insecure_email_lookup, див. Extended authentication settings та Using the same email address to login with different identity providers:

...
    grafana.ini:
      log:
        level: debug
        filters: "oauth.okta:debug authn:debug"
      auth:
        oauth_allow_insecure_email_lookup: true
...

І тепер все працює:

Готово.

Loading