tiktok-open-sdk 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 49fd72cee8903953251d98a9bd2952e08e11ebb480df678a3758214bc6aed326
4
- data.tar.gz: 53887f2fce24bf2784762a9a44acc497e5c6209eabcdc2c41809ec9b40f60016
3
+ metadata.gz: 804836be4e23b6a46c29c263b7b6d802cd2e6a70c61ed02a8f70328715649236
4
+ data.tar.gz: d2dee367fb303f07078a5e07a5a303f163db4895bb3e7468bbdeb2678308e1db
5
5
  SHA512:
6
- metadata.gz: 50bd4af46c1e7a05bdcfec937eb040e7d078ebc5b3a951fd1288cdb824fb32a20453e2e4527f9642345c4aefa6b9c9c83306dea78ee166e1c86b07bad6f0ceb2
7
- data.tar.gz: 8a8d4b6827e25a81720b30aee2f296fbaf0b328a0a57c7c9a3286c49de3e338a274b3b65f9b39bc67846113612542a5e7b22e74db99eda68d011519441077195
6
+ metadata.gz: 860dfd7da6a964785d661f1e6c9b2fd84bb324f1c8511207c0b3f981239b5d361490b8c410f31d0600bd5b4302c21b3b0cc69e370ad80814c1dc24c05b03ab06
7
+ data.tar.gz: 7707f1602f9c088c7d84aff5c20101138fd30e9bfb91569f13062c7e56591361fe4b1ecff06ee6b26b82ee89f362ffb2b5244fb4823c7989b8d9c125df9f9bd2
data/.rubocop.yml CHANGED
@@ -7,6 +7,9 @@ AllCops:
7
7
  NewCops: enable
8
8
  SuggestExtensions: false
9
9
 
10
+ Naming/VariableNumber:
11
+ EnforcedStyle: snake_case
12
+
10
13
  Metrics/MethodLength:
11
14
  Max: 15
12
15
 
data/.yardopts ADDED
@@ -0,0 +1,12 @@
1
+ --markup markdown
2
+ --markup-provider redcarpet
3
+ --output-dir doc/
4
+ --no-private
5
+ --protected
6
+ --exclude spec/
7
+ --exclude coverage/
8
+ --exclude tmp/
9
+ --exclude pkg/
10
+ --exclude .github/
11
+ --title "TikTok Open SDK Documentation"
12
+ --main README.md
data/CHANGELOG.md CHANGED
@@ -6,6 +6,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.4.0] - 2025-10-06
10
+ ### Added
11
+ - **OmniAuth Strategy** - Ready-to-use OmniAuth strategy for TikTok Open Platform integration in Rails applications
12
+ - Supports multiple TikTok OAuth scopes (user.info.basic, user.info.profile, user.info.stats)
13
+ - Automatic token handling and user info retrieval
14
+ - Rails/Devise integration examples and callbacks
15
+ - **Post API** - New module for interacting with TikTok's Post API endpoints
16
+ - Creator info query functionality for video publishing workflows
17
+ - Support for querying creator settings and capabilities
18
+
19
+ ## [0.3.0] - 2025-09-20
20
+ ### Added
21
+ - User information retrieval from TikTok API
22
+ - Improved request validation and error handling
23
+
9
24
  ## [0.2.0] - 2025-09-17
10
25
  ### Added
11
26
  - Support for obtaining a client access token directly from the TikTok Open API, enabling secure server-to-server authentication for backend integrations and service authentication flows.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # TikTok Open SDK
2
2
 
3
- [![Gem Version](https://img.shields.io/badge/gem-v0.2.0-blue.svg)](https://rubygems.org/gems/tiktok-open-sdk)
3
+ [![Gem Version](https://img.shields.io/badge/gem-v0.4.0-blue.svg)](https://rubygems.org/gems/tiktok-open-sdk)
4
4
  [![Ruby Version](https://img.shields.io/badge/ruby-%3E%3D%203.0.0-red.svg)](https://www.ruby-lang.org/en/downloads/)
5
5
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE.txt)
6
6
  [![CI](https://github.com/pochkuntaras/tiktok-open-sdk/actions/workflows/main.yml/badge.svg)](https://github.com/pochkuntaras/tiktok-open-sdk/actions/workflows/main.yml)
@@ -10,8 +10,11 @@ A comprehensive Ruby SDK for integrating with TikTok Open API. This gem provides
10
10
  ## Features
11
11
 
12
12
  - **OAuth 2.0 Authentication** – Seamless OAuth flow for secure integration
13
+ - **OmniAuth Strategy** – Ready-to-use OmniAuth strategy for Rails applications
13
14
  - **Client Authentication** – Server-to-server authentication with client credentials
14
15
  - **Token Management** – Easy access token exchange and refresh
16
+ - **User API** – Convenient methods to access user information
17
+ - **Post API** – Methods for querying creator information and video publishing
15
18
  - **HTTP Client** – Built-in client for interacting with TikTok APIs
16
19
 
17
20
  ## Installation
@@ -54,6 +57,11 @@ Tiktok::Open::Sdk.configure do |config|
54
57
  config.user_auth.auth_url = 'https://www.tiktok.com/v2/auth/authorize/'
55
58
  config.user_auth.token_url = 'https://open.tiktokapis.com/v2/oauth/token/'
56
59
  config.user_auth.revoke_token_url = 'https://open.tiktokapis.com/v2/oauth/revoke/'
60
+ config.user_info_url = 'https://open.tiktokapis.com/v2/user/info/'
61
+ config.creator_info_query_url = 'https://open.tiktokapis.com/v2/post/publish/creator_info/query/'
62
+
63
+ # Optional: Enable OmniAuth strategy auto-loading
64
+ config.load_omniauth = true
57
65
  end
58
66
  ```
59
67
 
@@ -156,12 +164,132 @@ end
156
164
 
157
165
  **Note:** Client tokens are used for server-to-server authentication and have different scopes and permissions than user tokens.
158
166
 
167
+ ### Using the Post API
168
+
169
+ The SDK provides convenient methods for interacting with TikTok's Post API:
170
+
171
+ #### Creator Info Query
172
+
173
+ Query creator information for video publishing:
174
+
175
+ ```ruby
176
+ # Get creator information
177
+ response = Tiktok::Open::Sdk.post.creator_info_query(access_token: access_token)
178
+
179
+ if response[:success]
180
+ creator_data = response[:response][:data]
181
+
182
+ puts "Creator Avatar: #{creator_data[:creator_avatar_url]}"
183
+ puts "Creator Nickname: #{creator_data[:creator_nickname]}"
184
+ puts "Max Video Duration: #{creator_data[:max_video_post_duration_sec]} seconds"
185
+ puts "Privacy Options: #{creator_data[:privacy_level_options]}"
186
+ else
187
+ puts "Error: #{response[:response][:error][:message]}"
188
+ end
189
+ ```
190
+
191
+ ### Using the User API
192
+
193
+ The SDK provides a convenient way to access user information:
194
+
195
+ ```ruby
196
+ # Get user information
197
+ response = Tiktok::Open::Sdk.user.get_user_info(
198
+ access_token: access_token,
199
+ fields: %w[open_id union_id avatar_url display_name]
200
+ )
201
+
202
+ if response[:success]
203
+ user_data = response[:response][:data][:user]
204
+
205
+ puts "User ID: #{user_data[:open_id]}"
206
+ puts "Display Name: #{user_data[:display_name]}"
207
+ else
208
+ puts "Error: #{response[:response][:error][:message]}"
209
+ end
210
+ ```
211
+
212
+ Available user fields include:
213
+ - `open_id` - User's Open ID
214
+ - `union_id` - User's Union ID
215
+ - `avatar_url` - User's avatar URL
216
+ - `display_name` - User's display name
217
+ - `username` - User's username
218
+ - And more (see documentation for full list)
219
+
220
+ ### Using OmniAuth Strategy
221
+
222
+ The SDK provides a ready-to-use OmniAuth strategy for Rails applications:
223
+
224
+ #### Rails Setup
225
+
226
+ Add the OmniAuth strategy to your Rails application:
227
+
228
+ ```ruby
229
+ # config/initializers/devise.rb
230
+ Devise.setup do |config|
231
+ config.omniauth(
232
+ :tiktok_open_sdk,
233
+ Rails.application.credentials.dig(:tiktok, :client_key),
234
+ Rails.application.credentials.dig(:tiktok, :client_secret),
235
+ scope: 'user.info.basic,user.info.profile,user.info.stats',
236
+ )
237
+ end
238
+ ```
239
+
240
+ Or use the SDK configuration:
241
+
242
+ ```ruby
243
+ # config/initializers/tiktok_sdk.rb
244
+ Tiktok::Open::Sdk.configure do |config|
245
+ config.client_key = Rails.application.credentials.dig(:tiktok, :client_key)
246
+ config.client_secret = Rails.application.credentials.dig(:tiktok, :client_secret)
247
+ config.user_auth.scopes = %w[user.info.basic video.publish]
248
+ config.user_auth.redirect_uri = "#{Rails.application.config.action_mailer.asset_host.chomp('/')}/users/auth/tiktok_open_sdk/callback"
249
+ config.load_omniauth = true
250
+ end
251
+ ```
252
+
253
+ #### OmniAuth Callback
254
+
255
+ Handle the OmniAuth callback in your controller:
256
+
257
+ ```ruby
258
+ # app/controllers/omniauth_callbacks_controller.rb
259
+ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
260
+ def tiktok_open_sdk
261
+ @auth = request.env['omniauth.auth']
262
+
263
+ # Find or create user based on TikTok auth data
264
+ @user = User.find_for_oauth(@auth)
265
+
266
+ # ...
267
+
268
+ if @user.persisted?
269
+ sign_in_and_redirect @user, event: :authentication
270
+ set_flash_message :notice, :success, kind: 'TikTok'
271
+ else
272
+ session['devise.provider_data'] = @auth.except('extra')
273
+ redirect_to new_user_registration_url
274
+ end
275
+ end
276
+ end
277
+ ```
278
+
279
+ #### Supported Scopes
280
+
281
+ The OmniAuth strategy supports the following TikTok scopes:
282
+
283
+ - `user.info.basic` - Basic user info: open_id, union_id, display_name, avatar URLs
284
+ - `user.info.profile` - Profile info: username, bio_description, profile_deep_link, is_verified
285
+ - `user.info.stats` - Statistics: follower_count, following_count, likes_count, video_count
286
+
159
287
  ### Using the HTTP Client
160
288
 
161
289
  The SDK includes a flexible HTTP client for making API calls:
162
290
 
163
291
  ```ruby
164
- # GET request
292
+ # GET request using request method
165
293
  response = Tiktok::Open::Sdk::HttpClient.request(
166
294
  :get,
167
295
  'https://open.tiktokapis.com/v2/user/info/',
@@ -173,6 +301,17 @@ response = Tiktok::Open::Sdk::HttpClient.request(
173
301
  }
174
302
  )
175
303
 
304
+ # GET request using get method
305
+ response = Tiktok::Open::Sdk::HttpClient.get(
306
+ 'https://open.tiktokapis.com/v2/user/info/',
307
+ params: {
308
+ fields: 'open_id,union_id,avatar_url'
309
+ },
310
+ headers: {
311
+ 'Authorization' => "Bearer #{access_token}"
312
+ }
313
+ )
314
+
176
315
  # POST request
177
316
  response = Tiktok::Open::Sdk::HttpClient.post(
178
317
  'https://open.tiktokapis.com/v2/video/list/',
@@ -241,6 +380,18 @@ class TiktokAuthController < ApplicationController
241
380
  session[:tiktok_access_token] = token_data[:access_token]
242
381
  session[:tiktok_refresh_token] = token_data[:refresh_token]
243
382
 
383
+ # Fetch user information
384
+ user_response = Tiktok::Open::Sdk::OpenApi::User.get_user_info(
385
+ access_token: token_data[:access_token],
386
+ fields: %w[open_id display_name avatar_url]
387
+ )
388
+
389
+ if user_response[:success]
390
+ user_data = user_response[:response][:data][:user]
391
+ session[:tiktok_user_name] = user_data[:display_name]
392
+ session[:tiktok_avatar] = user_data[:avatar_url]
393
+ end
394
+
244
395
  redirect_to dashboard_path, notice: 'Successfully connected to TikTok!'
245
396
  else
246
397
  redirect_to root_path, alert: 'Failed to authenticate with TikTok'
@@ -276,6 +427,7 @@ Tiktok::Open::Sdk.configure do |config|
276
427
  config.client_secret = 'your_client_secret' # Required
277
428
  config.user_auth.scopes = %w[user.info.basic] # Optional
278
429
  config.user_auth.redirect_uri = 'https://...' # Optional
430
+ config.user_info_url = 'https://open.tiktokapis.com/v2/user/info/' # Optional
279
431
  end
280
432
  ```
281
433
 
@@ -341,6 +493,56 @@ Obtains a client access token for server-to-server authentication.
341
493
  }
342
494
  ```
343
495
 
496
+ ### User API
497
+
498
+ #### `get_user_info(access_token:, fields:, validate: true)`
499
+
500
+ Retrieves user information from the TikTok Open API.
501
+
502
+ **Parameters:**
503
+ - `access_token` (String, required) - OAuth2 access token for authentication
504
+ - `fields` (Array<String>, required) - User fields to retrieve (must be valid fields)
505
+ - `validate` (Boolean, optional) - Whether to validate the token and fields (default: true)
506
+
507
+ **Returns:** Hash with `:success`, `:code`, and `:response` keys
508
+
509
+ **Available Fields:**
510
+ - `open_id` - Unique identifier for the user within the current application
511
+ - `union_id` - Persistent identifier for the user across different applications from the same developer
512
+ - `avatar_url` - URL to the user's profile image
513
+ - `avatar_url_100` - URL to the user's profile image in 100x100 pixel size
514
+ - `avatar_large_url` - URL to the user's profile image in higher resolution
515
+ - `display_name` - User's display name shown on their TikTok profile
516
+ - `bio_description` - User's biography text (if available)
517
+ - `profile_deep_link` - Direct link to the user's TikTok profile page
518
+ - `is_verified` - Boolean indicating if the account is verified by TikTok
519
+ - `username` - User's unique TikTok username
520
+ - `follower_count` - Number of followers the user has
521
+ - `following_count` - Number of accounts the user is following
522
+ - `likes_count` - Total number of likes received across all user's videos
523
+ - `video_count` - Total number of publicly posted videos by the user
524
+
525
+ ### Post API
526
+
527
+ #### `creator_info_query(access_token:)`
528
+
529
+ Queries creator information from the TikTok Open API for video publishing.
530
+
531
+ **Parameters:**
532
+ - `access_token` (String, required) - OAuth2 access token for authentication
533
+
534
+ **Returns:** Hash with `:success`, `:code`, and `:response` keys
535
+
536
+ **Response Data:**
537
+ - `creator_avatar_url` - Creator's avatar URL
538
+ - `creator_nickname` - Creator's display name
539
+ - `creator_username` - Creator's username
540
+ - `stitch_disabled` - Whether stitch is disabled for the creator
541
+ - `comment_disabled` - Whether comments are disabled for the creator
542
+ - `duet_disabled` - Whether duet is disabled for the creator
543
+ - `max_video_post_duration_sec` - Maximum video duration in seconds
544
+ - `privacy_level_options` - Available privacy level options
545
+
344
546
  ### HTTP Client
345
547
 
346
548
  #### `request(method, url, params: {}, headers: {}, body: nil)`
@@ -356,6 +558,17 @@ Performs HTTP requests.
356
558
 
357
559
  **Returns:** `Net::HTTPResponse` object
358
560
 
561
+ #### `get(url, params: {}, headers: {})`
562
+
563
+ Convenience method for GET requests.
564
+
565
+ **Parameters:**
566
+ - `url` (String) - Request URL
567
+ - `params` (Hash, optional) - Query parameters
568
+ - `headers` (Hash, optional) - HTTP headers
569
+
570
+ **Returns:** `Net::HTTPResponse` object
571
+
359
572
  #### `post(url, params: {}, headers: {}, body: nil)`
360
573
 
361
574
  Convenience method for POST requests.
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tiktok
4
+ module Open
5
+ module Omniauth
6
+ module Strategies
7
+ # OmniAuth strategy for TikTok Open Platform.
8
+ #
9
+ # Integrates TikTok OAuth2 authentication with OmniAuth.
10
+ #
11
+ # @example
12
+ # use OmniAuth::Builder do
13
+ # provider :tiktok_open, 'CLIENT_KEY', 'CLIENT_SECRET'
14
+ # end
15
+ #
16
+ # Supported scopes and their user info fields:
17
+ # - user.info.basic: open_id, union_id, display_name, avatar_url, avatar_url_100, avatar_large_url
18
+ # - user.info.profile: profile_deep_link, bio_description, is_verified, username
19
+ # - user.info.stats: follower_count, following_count, likes_count, video_count
20
+ class TiktokOpenSdk < ::OmniAuth::Strategies::OAuth2
21
+ # Custom access token class for TikTok.
22
+ class AccessToken < ::OAuth2::AccessToken; end
23
+
24
+ # Maps TikTok OAuth scopes to user info fields.
25
+ SCOPE_FIELDS = {
26
+ 'user.info.basic' => %w[open_id union_id display_name avatar_url avatar_url_100 avatar_large_url],
27
+ 'user.info.profile' => %w[profile_deep_link bio_description is_verified username],
28
+ 'user.info.stats' => %w[follower_count following_count likes_count video_count]
29
+ }.freeze
30
+
31
+ private_constant :SCOPE_FIELDS
32
+
33
+ # The name of this OmniAuth strategy.
34
+ option :name, :tiktok_open_sdk
35
+
36
+ # OAuth2 client options for TikTok endpoints.
37
+ # - :site: TikTok Open API base URL
38
+ # - :authorize_url: TikTok OAuth2 authorization endpoint
39
+ # - :token_url: TikTok OAuth2 token endpoint
40
+ option :client_options, {
41
+ site: ::Tiktok::Open::Sdk::Config::OPEN_API_BASE_URL,
42
+ authorize_url: ::Tiktok::Open::Sdk.config.user_auth.auth_url,
43
+ token_url: ::Tiktok::Open::Sdk.config.user_auth.token_url,
44
+ auth_scheme: :request_body,
45
+ auth_token_class: AccessToken
46
+ }
47
+
48
+ # List of parameters allowed in the authorization request.
49
+ option :authorize_options, %i[scope state redirect_uri]
50
+
51
+ # Default scope and redirect_uri from SDK config.
52
+ option :scope, ::Tiktok::Open::Sdk.config.user_auth.scopes.join(',')
53
+ option :redirect_uri, ::Tiktok::Open::Sdk.config.user_auth.redirect_uri
54
+
55
+ # Returns the unique TikTok user ID (open_id).
56
+ #
57
+ # @return [String] TikTok user's open_id.
58
+ uid { raw_info[:open_id].to_s }
59
+
60
+ # Returns a hash of user information.
61
+ #
62
+ # @return [Hash] User info with :name and :image keys, and profile fields if scope is present.
63
+ info do
64
+ { name: raw_info[:display_name], image: raw_info[:avatar_url_100] }.tap do |info|
65
+ if request_scopes.include?('user.info.profile')
66
+ info.merge!(raw_info.slice(:username, :bio_description, :profile_deep_link))
67
+ end
68
+ end
69
+ end
70
+
71
+ # Returns extra raw user information from TikTok.
72
+ #
73
+ # @return [Hash] Raw user info data from TikTok API.
74
+ extra { raw_info }
75
+
76
+ # Returns the callback URL without query parameters.
77
+ #
78
+ # @return [String] Callback URL.
79
+ def callback_url
80
+ super.split('?').first
81
+ end
82
+
83
+ # Builds the access token from TikTok's token endpoint.
84
+ #
85
+ # @raise [OAuth2::Error] if the token response is unsuccessful.
86
+ # @return [AccessToken] OAuth2 access token object.
87
+ def build_access_token
88
+ response = fetch_access_token
89
+ validate_token_response(response)
90
+ create_access_token(response[:response])
91
+ end
92
+
93
+ # Handles the initial OAuth2 request phase.
94
+ #
95
+ # @raise [ArgumentError] if client_secret is present in params.
96
+ def request_phase
97
+ params = authorize_params.merge('response_type' => 'code')
98
+
99
+ if params.key?(:client_secret) || params.key?('client_secret')
100
+ raise ArgumentError, 'client_secret is not allowed in authorize URL query params'
101
+ end
102
+
103
+ redirect client.authorize_url(params)
104
+ end
105
+
106
+ # Builds the authorization parameters for the OAuth2 request,
107
+ # adding the TikTok client_key.
108
+ #
109
+ # @return [Hash] Authorization parameters.
110
+ def authorize_params
111
+ super.tap do |params|
112
+ params[:client_key] = options.client_id
113
+ end
114
+ end
115
+
116
+ private
117
+
118
+ # Fetches access token from TikTok API
119
+ #
120
+ # @return [Hash] Token response from TikTok
121
+ def fetch_access_token
122
+ Tiktok::Open::Sdk.user_auth.fetch_access_token(
123
+ code: request.params['code'],
124
+ redirect_uri: callback_url
125
+ )
126
+ end
127
+
128
+ # Validates the token response
129
+ #
130
+ # @param response [Hash] Token response
131
+ # @raise [OAuth2::Error] if response is unsuccessful
132
+ def validate_token_response(response)
133
+ raise OAuth2::Error, response[:response] unless response[:success]
134
+ end
135
+
136
+ # Creates AccessToken from response data
137
+ #
138
+ # @param data [Hash] Token data from response
139
+ # @return [AccessToken] OAuth2 access token object
140
+ def create_access_token(data)
141
+ AccessToken.from_hash(
142
+ client,
143
+ access_token: data[:access_token],
144
+ refresh_token: data[:refresh_token],
145
+ expires_at: Time.now.to_i + data[:expires_in].to_i
146
+ )
147
+ end
148
+
149
+ # Returns the list of requested OAuth scopes.
150
+ #
151
+ # @return [Array<String>] List of scope strings.
152
+ def request_scopes
153
+ @request_scopes ||= request.params.fetch('scopes', 'user.info.basic').split(',')
154
+ end
155
+
156
+ # Returns the list of user info fields to request from TikTok,
157
+ # based on the requested scopes.
158
+ #
159
+ # @return [Array<String>] List of user info field names.
160
+ def user_info_fields
161
+ request_scopes.flat_map { |scope| SCOPE_FIELDS[scope] }.compact
162
+ end
163
+
164
+ # Fetches and memoizes the raw user info from the TikTok API.
165
+ #
166
+ # @return [Hash] Raw user info data, or empty hash if unavailable.
167
+ def raw_info
168
+ @raw_info ||= Tiktok::Open::Sdk.user.get_user_info(
169
+ access_token: access_token.token,
170
+ fields: user_info_fields
171
+ ).dig(:response, :data, :user) || {}
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
@@ -13,44 +13,71 @@ module Tiktok
13
13
  # config.client_secret = 'your_secret'
14
14
  # config.user_auth.scopes = %w[user.info.basic]
15
15
  class Config
16
- # @return [String] The TikTok client key.
16
+ # Base domains for constructing TikTok API URLs.
17
+ AUTH_BASE_URL = 'https://www.tiktok.com'
18
+ OPEN_API_BASE_URL = 'https://open.tiktokapis.com'
19
+
20
+ # @!attribute [rw] client_key
21
+ # @return [String] TikTok client key.
17
22
  attr_accessor :client_key
18
23
 
19
- # @return [String] The TikTok client secret.
24
+ # @!attribute [rw] client_secret
25
+ # @return [String] TikTok client secret.
20
26
  attr_accessor :client_secret
21
27
 
22
- # @return [UserAuth] The user authentication configuration.
28
+ # @!attribute [rw] user_info_url
29
+ # @return [String] TikTok user info endpoint URL.
30
+ attr_accessor :user_info_url
31
+
32
+ # @!attribute [rw] creator_info_query_url
33
+ # @return [String] TikTok Query Creator Info endpoint URL.
34
+ attr_accessor :creator_info_query_url
35
+
36
+ # @!attribute [rw] user_auth
37
+ # @return [UserAuth] User authentication configuration.
23
38
  attr_accessor :user_auth
24
39
 
25
- # Initializes a new Config object with default user authentication settings.
40
+ # @!attribute [rw] load_omniauth
41
+ # @return [Boolean] Whether to automatically load OmniAuth strategy.
42
+ attr_accessor :load_omniauth
43
+
44
+ # Create a new Config with default user authentication settings.
26
45
  def initialize
27
- @user_auth = UserAuth.new
46
+ @user_info_url = "#{OPEN_API_BASE_URL}/v2/user/info/"
47
+ @creator_info_query_url = "#{OPEN_API_BASE_URL}/v2/post/publish/creator_info/query/"
48
+ @user_auth = UserAuth.new
49
+ @load_omniauth = false
28
50
  end
29
51
 
30
52
  # User authentication configuration for TikTok Open SDK.
31
53
  #
32
54
  # Holds OAuth URLs, scopes, and redirect URI.
33
55
  class UserAuth
34
- # @return [String] The OAuth authorization URL.
56
+ # @!attribute [rw] auth_url
57
+ # @return [String] OAuth authorization URL.
35
58
  attr_accessor :auth_url
36
59
 
37
- # @return [String] The OAuth token exchange URL.
38
- attr_accessor :token_url
39
-
40
- # @return [String] The OAuth token revoke URL.
60
+ # @!attribute [rw] revoke_token_url
61
+ # @return [String] OAuth token revocation URL.
41
62
  attr_accessor :revoke_token_url
42
63
 
43
- # @return [Array<String>] The list of OAuth scopes.
64
+ # @!attribute [rw] token_url
65
+ # @return [String] OAuth token exchange URL.
66
+ attr_accessor :token_url
67
+
68
+ # @!attribute [rw] scopes
69
+ # @return [Array<String>] List of OAuth scopes.
44
70
  attr_accessor :scopes
45
71
 
46
- # @return [String, nil] The OAuth redirect URI.
72
+ # @!attribute [rw] redirect_uri
73
+ # @return [String, nil] OAuth redirect URI.
47
74
  attr_accessor :redirect_uri
48
75
 
49
76
  # Initializes a new UserAuth object with default URLs and empty scopes.
50
77
  def initialize
51
- @auth_url = 'https://www.tiktok.com/v2/auth/authorize/'
52
- @revoke_token_url = 'https://open.tiktokapis.com/v2/oauth/revoke/'
53
- @token_url = 'https://open.tiktokapis.com/v2/oauth/token/'
78
+ @auth_url = "#{AUTH_BASE_URL}/v2/auth/authorize/"
79
+ @revoke_token_url = "#{OPEN_API_BASE_URL}/v2/oauth/revoke/"
80
+ @token_url = "#{OPEN_API_BASE_URL}/v2/oauth/token/"
54
81
  @scopes = []
55
82
  @redirect_uri = nil
56
83
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Tiktok
6
+ module Open
7
+ module Sdk
8
+ module Helpers
9
+ # Shared authentication helper methods for TikTok Open SDK auth modules.
10
+ module AuthHelper
11
+ private
12
+
13
+ # Returns the HTTP headers for requests.
14
+ #
15
+ # @return [Hash] The headers for HTTP requests.
16
+ def headers
17
+ {
18
+ 'Content-Type': 'application/x-www-form-urlencoded',
19
+ 'Cache-Control': 'no-cache'
20
+ }
21
+ end
22
+
23
+ # Returns the client credentials for authentication.
24
+ #
25
+ # @return [Hash] The client credentials.
26
+ def credentials
27
+ {
28
+ client_key: Tiktok::Open::Sdk.config.client_key,
29
+ client_secret: Tiktok::Open::Sdk.config.client_secret
30
+ }
31
+ end
32
+
33
+ # Returns the default query parameters for the authorization URI.
34
+ #
35
+ # @return [Hash] The default query parameters:
36
+ # - :client_key [String] The TikTok client key.
37
+ # - :response_type [String] Always 'code'.
38
+ # - :scope [String] Comma-separated scopes.
39
+ # - :redirect_uri [String] The redirect URI.
40
+ # - :state [nil] Default state is nil.
41
+ def authorization_uri_default_params
42
+ {
43
+ client_key: Tiktok::Open::Sdk.config.client_key,
44
+ response_type: 'code',
45
+ scope: Tiktok::Open::Sdk.config.user_auth.scopes.join(','),
46
+ redirect_uri: Tiktok::Open::Sdk.config.user_auth.redirect_uri,
47
+ state: nil
48
+ }
49
+ end
50
+
51
+ # render_response moved to ::Tiktok::Open::Sdk::ResponseHelpers
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tiktok
4
+ module Open
5
+ module Sdk
6
+ module Helpers
7
+ # Helper methods for formatting HTTP responses across the SDK.
8
+ module ResponseHelper
9
+ include ::Tiktok::Open::Sdk::Helpers::StringUtilsHelper
10
+
11
+ # Parses and formats the HTTP response.
12
+ #
13
+ # @param response [Net::HTTPResponse] The HTTP response object.
14
+ # @return [Hash] The formatted response with keys:
15
+ # - :success [Boolean] Whether the response is a Net::HTTPSuccess.
16
+ # - :code [Integer] HTTP status code.
17
+ # - :response [Hash] Parsed JSON body or a hash with the raw string if parsing fails.
18
+ def render_response(response)
19
+ {
20
+ success: response.is_a?(Net::HTTPSuccess),
21
+ code: response.code.to_i,
22
+ response: parse_json(response.body)
23
+ }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Tiktok
6
+ module Open
7
+ module Sdk
8
+ module Helpers
9
+ # Utility methods for string and JSON handling.
10
+ module StringUtilsHelper
11
+ # Parses a JSON string into a Ruby hash with symbolized keys.
12
+ #
13
+ # @param str [String] JSON string to parse.
14
+ # @return [Hash] Parsed hash with symbolized keys, or a hash with the raw string if parsing fails.
15
+ def parse_json(str)
16
+ JSON.parse(str, symbolize_names: true)
17
+ rescue JSON::ParserError
18
+ { raw: str }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tiktok
4
+ module Open
5
+ module Sdk
6
+ module Helpers
7
+ module Validators
8
+ # Provides token validation methods for TikTok Open SDK.
9
+ module TokenValidator
10
+ # Regular expression to validate tokens.
11
+ # Ensures the token consists of at least 10 printable characters.
12
+ TOKEN_REGEX = /\A[[:print:]]{10,}\z/
13
+
14
+ # Checks if the given token is a valid string of at least 10 printable characters.
15
+ #
16
+ # @param token [String] The token to check.
17
+ # @return [Boolean] true if the token is valid, false otherwise.
18
+ def valid_token?(token)
19
+ token.is_a?(String) && !token.empty? && TOKEN_REGEX.match?(token)
20
+ end
21
+
22
+ # Validates the given token and raises an error if it is invalid.
23
+ #
24
+ # @param token [String] The token to validate.
25
+ # @raise [::Tiktok::Open::Sdk::RequestValidationError] if the token is invalid.
26
+ # @return [void]
27
+ def validate_token!(token)
28
+ return if valid_token?(token)
29
+
30
+ raise ::Tiktok::Open::Sdk::RequestValidationError,
31
+ 'Invalid token format: must be at least 10 printable characters.'
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -13,8 +13,6 @@ module Tiktok
13
13
  module HttpClient
14
14
  extend self
15
15
 
16
- include StringUtils
17
-
18
16
  # Supported HTTP methods.
19
17
  SUPPORTED_METHODS = %i[get post].freeze
20
18
 
@@ -53,6 +51,16 @@ module Tiktok
53
51
  request(:post, url, params: params, headers: headers, body: body)
54
52
  end
55
53
 
54
+ # Performs a GET HTTP request.
55
+ #
56
+ # @param url [String] The request URL.
57
+ # @param params [Hash] Query parameters.
58
+ # @param headers [Hash] HTTP headers.
59
+ # @return [Net::HTTPResponse] The HTTP response object.
60
+ def get(url, params: {}, headers: {})
61
+ request(:get, url, params: params, headers: headers)
62
+ end
63
+
56
64
  private
57
65
 
58
66
  # Ensures the HTTP method is supported.
@@ -77,7 +85,7 @@ module Tiktok
77
85
  case content_type
78
86
  when 'application/x-www-form-urlencoded'
79
87
  request.set_form_data(body)
80
- when 'application/json'
88
+ when 'application/json', 'application/json; charset=UTF-8'
81
89
  request.body = body.to_json
82
90
  else
83
91
  raise ArgumentError, "Unsupported content type: #{content_type}"
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'uri'
4
-
5
3
  module Tiktok
6
4
  module Open
7
5
  module Sdk
@@ -11,7 +9,8 @@ module Tiktok
11
9
  module Client
12
10
  extend self
13
11
 
14
- include Helpers
12
+ include ::Tiktok::Open::Sdk::Helpers::AuthHelper
13
+ include ::Tiktok::Open::Sdk::Helpers::ResponseHelper
15
14
 
16
15
  # Fetches a client access token from the TikTok Open API.
17
16
  #
@@ -11,7 +11,8 @@ module Tiktok
11
11
  module User
12
12
  extend self
13
13
 
14
- include Helpers
14
+ include ::Tiktok::Open::Sdk::Helpers::AuthHelper
15
+ include ::Tiktok::Open::Sdk::Helpers::ResponseHelper
15
16
 
16
17
  # Constructs the TikTok OAuth authorization URI.
17
18
  #
@@ -22,7 +23,7 @@ module Tiktok
22
23
  # @return [URI] The constructed authorization URI.
23
24
  def authorization_uri(params = {})
24
25
  allowed_params = params.slice(:scope, :redirect_uri, :state)
25
- uri = URI(Tiktok::Open::Sdk.config.user_auth.auth_url)
26
+ uri = URI.parse(Tiktok::Open::Sdk.config.user_auth.auth_url)
26
27
  query_params = authorization_uri_default_params.merge(allowed_params)
27
28
  uri.query = URI.encode_www_form(query_params)
28
29
 
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Provides methods to interact with TikTok Open API post endpoints.
4
+ module Tiktok
5
+ module Open
6
+ module Sdk
7
+ module OpenApi
8
+ module Post
9
+ # Provides methods for handling TikTok Open API post endpoints.
10
+ module Publish
11
+ extend self
12
+
13
+ include ::Tiktok::Open::Sdk::Helpers::ResponseHelper
14
+ include ::Tiktok::Open::Sdk::Helpers::Validators::TokenValidator
15
+
16
+ # Queries creator information from the TikTok Open API.
17
+ #
18
+ # @param access_token [String] OAuth2 access token for authentication.
19
+ # @return [Hash] Parsed API response containing creator information.
20
+ # @raise [::Tiktok::Open::Sdk::RequestValidationError] If the access token is invalid.
21
+ #
22
+ # @example
23
+ # Tiktok::Open::Sdk.post.creator_info_query(access_token: 'your_access_token')
24
+ def creator_info_query(access_token:)
25
+ validate_token!(access_token)
26
+
27
+ render_response Tiktok::Open::Sdk::HttpClient.post(
28
+ Tiktok::Open::Sdk.config.creator_info_query_url,
29
+ headers: {
30
+ Authorization: "Bearer #{access_token}"
31
+ }
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Provides methods to interact with TikTok Open API user endpoints.
4
+ module Tiktok
5
+ module Open
6
+ module Sdk
7
+ module OpenApi
8
+ # Provides user-related methods for the TikTok Open API.
9
+ module User
10
+ # List of valid user info fields that can be requested from the API.
11
+ FIELDS = %w[
12
+ open_id
13
+ union_id
14
+ avatar_url
15
+ avatar_url_100
16
+ avatar_large_url
17
+ display_name
18
+ bio_description
19
+ profile_deep_link
20
+ is_verified
21
+ username
22
+ follower_count
23
+ following_count
24
+ likes_count
25
+ video_count
26
+ ].freeze
27
+
28
+ extend self
29
+
30
+ include ::Tiktok::Open::Sdk::Helpers::ResponseHelper
31
+ include ::Tiktok::Open::Sdk::Helpers::Validators::TokenValidator
32
+
33
+ # Retrieves user information from the TikTok Open API.
34
+ #
35
+ # @param access_token [String] OAuth2 access token for authentication.
36
+ # @param fields [Array<String>] User fields to retrieve. Must be a subset of FIELDS.
37
+ # @param validate [Boolean] Whether to validate the token and fields. Defaults to true.
38
+ # @return [Hash] Parsed API response containing user information.
39
+ # @raise [::Tiktok::Open::Sdk::RequestValidationError] If the access token or any requested field is invalid.
40
+ def get_user_info(access_token:, fields:, validate: true)
41
+ if validate
42
+ validate_token!(access_token)
43
+ validate_fields!(fields)
44
+ end
45
+
46
+ render_response Tiktok::Open::Sdk::HttpClient.get(
47
+ Tiktok::Open::Sdk.config.user_info_url,
48
+ params: {
49
+ fields: fields.join(',')
50
+ },
51
+ headers: {
52
+ Authorization: "Bearer #{access_token}"
53
+ }
54
+ )
55
+ end
56
+
57
+ private
58
+
59
+ # Ensures all requested fields are supported by the API.
60
+ #
61
+ # @param fields [Array<String>] Fields to validate against FIELDS.
62
+ # @raise [::Tiktok::Open::Sdk::RequestValidationError] If any field is not supported.
63
+ def validate_fields!(fields)
64
+ invalid = fields - FIELDS
65
+
66
+ return if invalid.empty?
67
+
68
+ raise ::Tiktok::Open::Sdk::RequestValidationError, "Invalid fields: #{invalid.join(", ")}"
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -3,7 +3,7 @@
3
3
  module Tiktok
4
4
  module Open
5
5
  module Sdk
6
- VERSION = '0.2.0'
6
+ VERSION = '0.4.0'
7
7
  end
8
8
  end
9
9
  end
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'sdk/string_utils'
4
-
5
- require_relative 'sdk/open_api/auth/helpers'
3
+ require_relative 'sdk/helpers/string_utils_helper'
4
+ require_relative 'sdk/helpers/response_helper'
5
+ require_relative 'sdk/helpers/auth_helper'
6
+ require_relative 'sdk/helpers/validators/token_validator'
6
7
  require_relative 'sdk/open_api/auth/user'
7
8
  require_relative 'sdk/open_api/auth/client'
9
+ require_relative 'sdk/open_api/post/publish'
10
+ require_relative 'sdk/open_api/user'
8
11
  require_relative 'sdk/version'
9
12
  require_relative 'sdk/http_client'
10
13
  require_relative 'sdk/config'
@@ -19,6 +22,8 @@ module Tiktok
19
22
  # raise Tiktok::Open::Sdk::Error, "Something went wrong"
20
23
  class Error < StandardError; end
21
24
 
25
+ class RequestValidationError < Error; end
26
+
22
27
  class << self
23
28
  # SDK configuration object
24
29
  #
@@ -41,12 +46,15 @@ module Tiktok
41
46
  # config.user_auth.token_url = 'https://open.tiktokapis.com/v2/oauth/token/'
42
47
  # config.user_auth.scopes = %w[user.info.basic video.list]
43
48
  # config.user_auth.redirect_uri = 'https://your-redirect-uri.example.com'
49
+ # config.load_omniauth = true
44
50
  # end
45
51
  def configure
46
52
  self.config ||= Config.new
47
53
 
48
54
  yield(config)
49
55
 
56
+ load_omniauth! if config.load_omniauth
57
+
50
58
  config
51
59
  end
52
60
 
@@ -69,6 +77,44 @@ module Tiktok
69
77
  def client_auth
70
78
  OpenApi::Auth::Client
71
79
  end
80
+
81
+ # Convenience accessor for post publish functionality
82
+ #
83
+ # @return [OpenApi::Post::Publish] the Post publish module
84
+ #
85
+ # @example
86
+ # Tiktok::Open::Sdk.post.video_init(access_token: 'token', post_info: post_info, source_info: source_info)
87
+ def post
88
+ OpenApi::Post::Publish
89
+ end
90
+
91
+ # Convenience accessor for user functionality
92
+ #
93
+ # @return [OpenApi::User] the User module
94
+ #
95
+ # @example
96
+ # Tiktok::Open::Sdk.user.info(access_token: 'token')
97
+ def user
98
+ OpenApi::User
99
+ end
100
+
101
+ private
102
+
103
+ # Loads the OmniAuth strategy for TikTok Open Platform.
104
+ #
105
+ # Attempts to require the necessary OmniAuth dependencies for TikTok Open integration.
106
+ # Raises an error if the required gems are not available.
107
+ #
108
+ # @raise [Tiktok::Open::Sdk::Error] if 'omniauth-oauth2' or the TikTok strategy cannot be loaded
109
+ # @example
110
+ # Tiktok::Open::Sdk.load_omniauth!
111
+ def load_omniauth!
112
+ require 'omniauth-oauth2'
113
+ require 'tiktok/open/omniauth/strategies/tiktok_open_sdk'
114
+ rescue LoadError => e
115
+ raise ::Tiktok::Open::Sdk::Error,
116
+ "OmniAuth is not loaded! Error: #{e.message}"
117
+ end
72
118
  end
73
119
  end
74
120
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tiktok-open-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Taras Pochkun
@@ -20,18 +20,24 @@ files:
20
20
  - ".rspec"
21
21
  - ".rubocop.yml"
22
22
  - ".tool-versions"
23
+ - ".yardopts"
23
24
  - CHANGELOG.md
24
25
  - CODE_OF_CONDUCT.md
25
26
  - LICENSE.txt
26
27
  - README.md
27
28
  - Rakefile
29
+ - lib/tiktok/open/omniauth/strategies/tiktok_open_sdk.rb
28
30
  - lib/tiktok/open/sdk.rb
29
31
  - lib/tiktok/open/sdk/config.rb
32
+ - lib/tiktok/open/sdk/helpers/auth_helper.rb
33
+ - lib/tiktok/open/sdk/helpers/response_helper.rb
34
+ - lib/tiktok/open/sdk/helpers/string_utils_helper.rb
35
+ - lib/tiktok/open/sdk/helpers/validators/token_validator.rb
30
36
  - lib/tiktok/open/sdk/http_client.rb
31
37
  - lib/tiktok/open/sdk/open_api/auth/client.rb
32
- - lib/tiktok/open/sdk/open_api/auth/helpers.rb
33
38
  - lib/tiktok/open/sdk/open_api/auth/user.rb
34
- - lib/tiktok/open/sdk/string_utils.rb
39
+ - lib/tiktok/open/sdk/open_api/post/publish.rb
40
+ - lib/tiktok/open/sdk/open_api/user.rb
35
41
  - lib/tiktok/open/sdk/version.rb
36
42
  - sig/tiktok/open/sdk.rbs
37
43
  homepage: https://github.com/pochkuntaras/tiktok-open-sdk
@@ -40,9 +46,10 @@ licenses:
40
46
  metadata:
41
47
  allowed_push_host: https://rubygems.org
42
48
  homepage_uri: https://github.com/pochkuntaras/tiktok-open-sdk
43
- source_code_uri: https://github.com/pochkuntaras/tiktok-open-sdk
49
+ source_code_uri: https://github.com/pochkuntaras/tiktok-open-sdk.git
44
50
  changelog_uri: https://github.com/pochkuntaras/tiktok-open-sdk/blob/main/CHANGELOG.md
45
51
  rubygems_mfa_required: 'true'
52
+ documentation_uri: https://rubydoc.info/gems/tiktok-open-sdk
46
53
  rdoc_options: []
47
54
  require_paths:
48
55
  - lib
@@ -1,73 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
-
5
- module Tiktok
6
- module Open
7
- module Sdk
8
- module OpenApi
9
- module Auth
10
- # Shared authentication helper methods for TikTok Open SDK auth modules.
11
- module Helpers
12
- include StringUtils
13
-
14
- private
15
-
16
- # Returns the HTTP headers for requests.
17
- #
18
- # @return [Hash] The headers for HTTP requests.
19
- def headers
20
- {
21
- 'Content-Type': 'application/x-www-form-urlencoded',
22
- 'Cache-Control': 'no-cache'
23
- }
24
- end
25
-
26
- # Returns the client credentials for authentication.
27
- #
28
- # @return [Hash] The client credentials.
29
- def credentials
30
- {
31
- client_key: Tiktok::Open::Sdk.config.client_key,
32
- client_secret: Tiktok::Open::Sdk.config.client_secret
33
- }
34
- end
35
-
36
- # Returns the default query parameters for the authorization URI.
37
- #
38
- # @return [Hash] The default query parameters:
39
- # - :client_key [String] The TikTok client key.
40
- # - :response_type [String] Always 'code'.
41
- # - :scope [String] Comma-separated scopes.
42
- # - :redirect_uri [String] The redirect URI.
43
- # - :state [nil] Default state is nil.
44
- def authorization_uri_default_params
45
- {
46
- client_key: Tiktok::Open::Sdk.config.client_key,
47
- response_type: 'code',
48
- scope: Tiktok::Open::Sdk.config.user_auth.scopes.join(','),
49
- redirect_uri: Tiktok::Open::Sdk.config.user_auth.redirect_uri,
50
- state: nil
51
- }
52
- end
53
-
54
- # Parses and formats the HTTP response.
55
- #
56
- # @param response [Net::HTTPResponse] The HTTP response object.
57
- # @return [Hash] The formatted response with keys:
58
- # - :success [Boolean] Whether the response is a Net::HTTPSuccess.
59
- # - :code [Integer] HTTP status code.
60
- # - :response [Hash] Parsed JSON body or a hash with the raw string if parsing fails.
61
- def render_response(response)
62
- {
63
- success: response.is_a?(Net::HTTPSuccess),
64
- code: response.code.to_i,
65
- response: parse_json(response.body)
66
- }
67
- end
68
- end
69
- end
70
- end
71
- end
72
- end
73
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
-
5
- module Tiktok
6
- module Open
7
- module Sdk
8
- # Utility methods for string and JSON handling.
9
- module StringUtils
10
- module_function
11
-
12
- # Parses a JSON string into a Ruby hash with symbolized keys.
13
- #
14
- # @param str [String] JSON string to parse.
15
- # @return [Hash] Parsed hash with symbolized keys, or a hash with the raw string if parsing fails.
16
- def parse_json(str)
17
- JSON.parse(str, symbolize_names: true)
18
- rescue JSON::ParserError
19
- { raw: str }
20
- end
21
- end
22
- end
23
- end
24
- end