### Development Setup for Discourse Plugin Source: https://github.com/kaktaknet/discourse-yandex-oauth/blob/main/README.md Commands to set up a development environment for the Discourse plugin, including database migration and starting the Rails server. ```bash bundle exec rake db:migrate bundle exec rails server ``` -------------------------------- ### Username Sanitization Examples Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt These examples demonstrate how usernames are sanitized for uniqueness and length constraints. Fallback usernames are generated for invalid or nil inputs. ```ruby # sanitize_username("super-admin_42") # => "super-admin_42" # sanitize_username("averylongnamefarexceedingmax") # => "averylongnamefarexc" # sanitize_username("ИванПетров") # => "yandex_user_4f3a2b" (random fallback) # sanitize_username(nil) # => "yandex_user_4f3a2b" (random fallback) ``` -------------------------------- ### Install Yandex OAuth Plugin Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt Clone the plugin repository into the Discourse app directory. This is a prerequisite for enabling the plugin. ```bash cd /var/discourse git clone https://github.com/kaktaknet/discourse-yandex-oauth.git ``` -------------------------------- ### Install Discourse Plugin via Git Clone Source: https://github.com/kaktaknet/discourse-yandex-oauth/blob/main/README.md Add the plugin to your Discourse installation by cloning the repository. Ensure this is added to the `hooks.after_code` section in your `app.yml`. ```yaml - git clone https://github.com/kaktaknet/discourse-yandex-oauth.git ``` ```yaml - git clone git@github.com:kaktaknet/discourse-yandex-oauth.git ``` -------------------------------- ### Run Plugin Tests Source: https://github.com/kaktaknet/discourse-yandex-oauth/blob/main/README.md Execute the plugin's specifications using the Rake task. Ensure the plugin is installed and configured correctly before running. ```bash bundle exec rake plugin:spec[discourse-yandex-oauth] ``` -------------------------------- ### Username Uniqueness Enforcement Example Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt This example illustrates how the system enforces username uniqueness by appending numerical suffixes when a username already exists in the database. ```ruby # Uniqueness enforcement (when "ivan_petrov" already exists): # ensure_unique_username("ivan_petrov") # checks DB → "ivan_petrov_1" # ensure_unique_username("ivan_petrov") # if _1 taken → "ivan_petrov_2" … up to _9999 ``` -------------------------------- ### View Plugin Logs Source: https://github.com/kaktaknet/discourse-yandex-oauth/blob/main/README.md Tail the application logs to filter for Yandex-related messages. This helps in diagnosing authentication issues. ```bash # Docker ./launcher logs app | grep Yandex ``` ```bash # Development tail -f log/development.log | grep Yandex ``` -------------------------------- ### Yandex OAuth Application Registration Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt Steps to register a Yandex OAuth application. This involves visiting the Yandex OAuth portal, creating an app, and setting the correct permissions and callback URL. ```bash # ── 1. Register a Yandex OAuth application ──────────────────────────────────── # Visit https://oauth.yandex.ru/client/new and create an app. # Required permissions: login:email | login:info | login:avatar # Callback URL to set: https://your-discourse.com/auth/yandex/callback ``` -------------------------------- ### Rebuild Discourse Container Source: https://github.com/kaktaknet/discourse-yandex-oauth/blob/main/README.md After modifying the `app.yml` file, rebuild your Discourse container to apply the changes. This command is run from the `/var/discourse` directory. ```bash cd /var/discourse ./launcher rebuild app ``` -------------------------------- ### Rebuild Discourse Docker Container Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt After adding the plugin, rebuild the Docker container to apply the changes. This command ensures the new plugin code is integrated into the running Discourse instance. ```bash ./launcher rebuild app ``` -------------------------------- ### Yandex API User Info Fetch cURL Equivalent Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt A cURL command to manually test fetching user information from the Yandex API. This is useful for debugging authentication issues. ```bash curl -H "Authorization: OAuth AgAAAAAz...." \ "https://login.yandex.ru/info?format=json" ``` -------------------------------- ### Yandex User Info Fetch Endpoint Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt Fetches user profile information from Yandex using the obtained access token. The `Authorization` header must include the `access_token`. ```http GET https://login.yandex.ru/info?format=json Authorization: OAuth AgAAAAAz.... ``` -------------------------------- ### Register Yandex Authentication Provider in Discourse Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt This code registers the Yandex OAuth provider with Discourse, sets up necessary site settings, loads the custom authenticator, and defines the UI elements for the login button. Ensure `yandex_enabled` site setting is configured before this code runs. ```ruby # plugin.rb # frozen_string_literal: true # name: discourse-yandex-oauth # about: Yandex ID OAuth2 authentication for Discourse # version: 1.0.0 # authors: Discourse community # url: https://github.com/kaktaknet/discourse-yandex-oauth # CRITICAL: enabled_site_setting MUST be before any require statements # If yandex_enabled is false the plugin loads nothing further. enabled_site_setting :yandex_enabled require_relative "lib/omniauth/strategies/yandex" require_relative "lib/yandex_authenticator" # Register the provider so Discourse shows the "Login with Yandex" button. auth_provider( title: "Yandex", authenticator: YandexAuthenticator.new, message: "Войти с помощью Яндекса", # shown inside the OAuth popup frame_width: 920, frame_height: 800, icon: "fab-yandex" ) register_svg_icon "fab-yandex" if respond_to?(:register_svg_icon) register_asset "stylesheets/yandex-auth.scss" ``` -------------------------------- ### Monitor Yandex Authentication Events Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt Tail the application logs to monitor Yandex authentication events. This helps in debugging and understanding the OAuth flow in both production and development environments. ```bash ./launcher logs app | grep -i yandex ``` ```bash tail -f log/development.log | grep -i yandex ``` -------------------------------- ### Configure Yandex OAuth Application Permissions Source: https://github.com/kaktaknet/discourse-yandex-oauth/blob/main/README.md Specify the required permissions when creating your Yandex OAuth application. These permissions grant the plugin access to user data needed for authentication. ```text ✅ login:email - Access to email address ✅ login:info - Access to user profile information ✅ login:avatar - Access to user avatar ``` -------------------------------- ### Discourse Yandex Authentication Settings Source: https://github.com/kaktaknet/discourse-yandex-oauth/blob/main/README.md Configuration options for the Yandex authentication plugin within Discourse admin settings. Enable the plugin and provide your Yandex OAuth client credentials. ```text yandex_enabled ✅ true - Enable Yandex authentication yandex_client_id a1b2c3d4e5f6... - OAuth Client ID from Yandex yandex_client_secret •••••• - OAuth Client Secret from Yandex yandex_email_verified ✅ true - Trust email verification from Yandex ``` -------------------------------- ### Yandex User Data Mapping to Discourse Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt Illustrates how Yandex API fields are mapped to Discourse user data during authentication. This includes UID, email, username, name, and additional Yandex-specific data. ```ruby # Mapping performed inside YandexAuthenticator#after_authenticate: # Yandex API field → Discourse destination Processing applied # ───────────────────────────────────────────────────────────────────────────── # raw_info['id'] → Auth::Result uid Stored as string # raw_info['default_email'] → result.email Direct assignment # SiteSetting.yandex_email_verified → result.email_valid Boolean site setting # raw_info['login'] → result.username sanitize_username() # "first_name last_name" → result.name String interpolation # raw_info['id'] → extra_data[:yandex_user_id] For account linking # raw_info['login'] → extra_data[:yandex_login] Stored verbatim # raw_info['first_name'] → extra_data[:yandex_first_name] # raw_info['last_name'] → extra_data[:yandex_last_name] # Username sanitization examples: # sanitize_username("ivan.petrov") # => "ivan_petrov" ``` -------------------------------- ### Yandex OAuth Plugin Server Locale Configuration Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt Defines site settings for Yandex OAuth in Discourse's server-side locale file. This includes enabling the plugin and setting client credentials. ```yaml en: site_settings: yandex_enabled: "Enable Yandex authentication" yandex_client_id: "Yandex OAuth Client ID" yandex_client_secret: "Yandex OAuth Client Secret" yandex_email_verified: "Trust Yandex email verification" login: # Raised when Yandex returns a token but no email in the profile. authenticator_error_no_email: "Yandex did not return an email address" # Raised on network errors or non-2xx responses from login.yandex.ru/info. authenticator_error_fetch_failed: "Failed to fetch user details from Yandex" ``` -------------------------------- ### Yandex OAuth Site Settings Configuration Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt Defines the Discourse site settings required for Yandex OAuth integration. These settings control enablement, client credentials, and email verification trust. ```yaml # config/settings.yml plugins: yandex_enabled: default: false client: true # exposed to the Ember frontend description: "Enable Yandex authentication" yandex_client_id: default: "" description: "Yandex OAuth Client ID (get from https://oauth.yandex.ru)" regex: "^[a-f0-9]*$" # validated on save yandex_client_secret: default: "" secret: true # value masked in UI & logs description: "Yandex OAuth Client Secret" yandex_email_verified: default: true client: true description: "Trust email verification from Yandex (recommended: true)" # Admin panel path: Admin → Settings → Login → Yandex # Programmatic read (Ruby): # SiteSetting.yandex_enabled # => true/false # SiteSetting.yandex_client_id # => "a1b2c3d4..." # SiteSetting.yandex_client_secret # => "secret..." # SiteSetting.yandex_email_verified # => true/false ``` -------------------------------- ### Yandex OAuth Plugin Client Locale Configuration Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt Configures the login button label for Yandex authentication in the Discourse client-side locale file. This affects the text displayed on the login interface. ```yaml en: js: login: yandex: name: "Yandex" title: "with Yandex" # renders as "Log in with Yandex" ``` -------------------------------- ### Yandex Login Button Styling Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt Applies the Yandex brand color to the social login button icon using SCSS. This ensures visual consistency with Yandex's branding. ```scss // assets/stylesheets/yandex-auth.scss .btn-social.yandex { // Yandex official brand color applied to the Font Awesome Yandex icon. .d-icon-fab-yandex { color: #FC3F1D; } // Slightly darker on hover for visual feedback. &:hover .d-icon-fab-yandex { color: #E03919; } } ``` -------------------------------- ### Yandex Avatar Fetch Endpoint Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt Fetches a user's avatar from Yandex. This is an optional step performed post-authentication to import the user's avatar into Discourse. ```http GET https://avatars.yandex.net/get-yapic/abc123def456/islands-200 ``` -------------------------------- ### Yandex API Endpoints Reference Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt The four external Yandex API endpoints consumed by the plugin during a complete OAuth cycle. ```APIDOC ## GET https://oauth.yandex.ru/authorize ### Description Authorization redirect endpoint for the OAuth flow. The user is redirected here to log in and grant permissions. ### Method GET ### Endpoint https://oauth.yandex.ru/authorize ### Query Parameters - **response_type** (string) - Required - Should be 'code' - **client_id** (string) - Required - Your application's client ID - **redirect_uri** (string) - Required - The URI to redirect to after authorization - **state** (string) - Required - A CSRF token for security ``` ```APIDOC ## POST https://oauth.yandex.ru/token ### Description Token exchange endpoint. Used to exchange an authorization code for an access token. ### Method POST ### Endpoint https://oauth.yandex.ru/token ### Request Body - **grant_type** (string) - Required - Should be 'authorization_code' - **code** (string) - Required - The authorization code received from the redirect - **client_id** (string) - Required - Your application's client ID - **client_secret** (string) - Required - Your application's client secret ### Response #### Success Response (200) - **access_token** (string) - The access token for API requests - **token_type** (string) - The type of token, usually 'bearer' - **expires_in** (integer) - The token's expiration time in seconds ``` ```APIDOC ## GET https://login.yandex.ru/info ### Description User info fetch endpoint. Retrieves user profile information using an access token. ### Method GET ### Endpoint https://login.yandex.ru/info ### Query Parameters - **format** (string) - Required - Should be 'json' ### Headers - **Authorization** (string) - Required - OAuth token in the format 'OAuth YOUR_ACCESS_TOKEN' ### Response #### Success Response (200) - **id** (string) - Unique user ID - **login** (string) - User's Yandex login - **default_email** (string) - User's default email address - **first_name** (string) - User's first name - **last_name** (string) - User's last name - **default_avatar_id** (string) - User's default avatar ID ``` ```APIDOC ## GET https://avatars.yandex.net/get-yapic/{avatar_id}/{size} ### Description Avatar fetch endpoint. Retrieves a user's avatar image. ### Method GET ### Endpoint https://avatars.yandex.net/get-yapic/{avatar_id}/{size} ### Path Parameters - **avatar_id** (string) - Required - The user's avatar ID - **size** (string) - Required - The desired size of the avatar (e.g., 'islands-200') ### Response #### Success Response (200) - Returns a JPEG image of the user's avatar. ``` -------------------------------- ### Log Yandex Authentication Events Source: https://github.com/kaktaknet/discourse-yandex-oauth/blob/main/README.md Log authentication events for Yandex. This is useful for debugging and monitoring. Ensure Rails logger is configured. ```ruby Rails.logger.error("Yandex API error: HTTP 401") ``` ```ruby Rails.logger.error("Yandex connection error: timeout") ``` -------------------------------- ### YandexAuthenticator Ruby Class Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt Handles Yandex OAuth authentication flow within Discourse. It defines methods for enabling the provider, registering the OmniAuth strategy, and processing the authentication callback. ```ruby class YandexAuthenticator < Auth::ManagedAuthenticator # Identifier used by Discourse and OmniAuth to reference this provider. def name 'yandex' end # Checks the yandex_enabled site setting at runtime. def enabled? SiteSetting.yandex_enabled end # Registers the :yandex OmniAuth strategy with the client credentials # stored in Discourse site settings. def register_middleware(omniauth) omniauth.provider :yandex, SiteSetting.yandex_client_id, SiteSetting.yandex_client_secret end # Called after the OAuth callback succeeds. # auth_token structure (from OmniAuth): # auth_token.uid => "123456789" # auth_token.info.email => "ivan.petrov@yandex.ru" # auth_token.info.login => "ivan.petrov" # auth_token.info.name => "Ivan Petrov" def after_authenticate(auth_token, existing_account: nil) result = super # handles account lookup / creation via ManagedAuthenticator result.email = auth_token.info.email result.email_valid = SiteSetting.yandex_email_verified result.username = sanitize_username(auth_token.info.login || auth_token.info.first_name) result.name = auth_token.info.name # Persisted in UserAssociatedAccount#extra_data (JSON column). result.extra_data = { yandex_user_id: auth_token.uid.to_s, yandex_login: auth_token.info.login, yandex_first_name: auth_token.info.first_name, yandex_last_name: auth_token.info.last_name } result end # Returns true when Discourse should treat the returned email as verified. def primary_email_verified?(auth_token) SiteSetting.yandex_email_verified end private # Sanitizes a raw Yandex login/name string into a valid Discourse username: # "ivan.petrov" => "ivan_petrov" (dot replaced) # "ИванПетров" => "______" (all non-ASCII replaced, then random fallback) # "averylongnameexceedingtwentycharacters" => "averylongnameexceedin" (truncated) def sanitize_username(username) return generate_random_username if username.blank? username = username.gsub(/[^a-zA-Z0-9_-]/, '_') username = username[0...20] username = generate_random_username if username.blank? ensure_unique_username(username) end # Appends an incrementing counter (_1, _2, …) until the username is unique. # Gives up after 9999 attempts (extremely unlikely edge case). def ensure_unique_username(username) original = username counter = 1 while User.exists?(username: username) max_base_length = 20 - "_#{counter}".length base = original[0...max_base_length] username = "#{base}_#{counter}" counter += 1 break if counter > 9999 end username end # Fallback: produces e.g. "yandex_user_4f3a2b1c9e0d" def generate_random_username "yandex_user_#{SecureRandom.hex(6)}" end end ``` -------------------------------- ### Yandex Authorization Redirect Endpoint Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt This endpoint is used in the OAuth flow to redirect the user to Yandex for login and permission granting. Ensure `client_id` and `redirect_uri` are correctly set. ```http GET https://oauth.yandex.ru/authorize ?response_type=code &client_id=YOUR_CLIENT_ID &redirect_uri=https://your-discourse.com/auth/yandex/callback &state=CSRF_TOKEN ``` -------------------------------- ### Custom OmniAuth Strategy for Yandex Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt This Ruby code defines a custom OmniAuth OAuth2 strategy for Yandex. It configures the Yandex API endpoints, extracts user information (ID, email, name, login) from the Yandex API response, and normalizes it for Discourse's authentication pipeline. The `raw_info` method fetches user details from `https://login.yandex.ru/info`. ```ruby # lib/omniauth/strategies/yandex.rb # frozen_string_literal: true require 'omniauth-oauth2' module OmniAuth module Strategies class Yandex < OmniAuth::Strategies::OAuth2 option :name, 'yandex' option :client_options, { site: 'https://login.yandex.ru', authorize_url: 'https://oauth.yandex.ru/authorize', token_url: 'https://oauth.yandex.ru/token' } # Yandex numeric user ID becomes the OmniAuth uid. uid { raw_info['id'] } # Normalized info hash consumed by YandexAuthenticator#after_authenticate. info do { email: raw_info['default_email'], name: "#{raw_info['first_name']} #{raw_info['last_name']}".strip, first_name: raw_info['first_name'], last_name: raw_info['last_name'], login: raw_info['login'] # Yandex username, e.g. "ivan.petrov" } end extra { { raw_info: raw_info } } # GET https://login.yandex.ru/info?format=json # Example response: # { # "id": "123456789", # "login": "ivan.petrov", # "default_email": "ivan.petrov@yandex.ru", # "first_name": "Ivan", # "last_name": "Petrov", # "default_avatar_id": "abc123" # } def raw_info @raw_info ||= access_token.get('/info?format=json').parsed end # Builds the full callback URL, e.g.: # https://your-discourse.com/auth/yandex/callback def callback_url full_host + script_name + callback_path end end end end ``` -------------------------------- ### Yandex Token Exchange Endpoint Source: https://context7.com/kaktaknet/discourse-yandex-oauth/llms.txt This endpoint exchanges the authorization code for an access token. It requires `grant_type`, `code`, `client_id`, and `client_secret`. ```http POST https://oauth.yandex.ru/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code &code=AUTHORIZATION_CODE &client_id=YOUR_CLIENT_ID &client_secret=YOUR_CLIENT_SECRET ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.