the browser-facing portion of osu!
at master 565 lines 20 kB view raw
1{{-- 2 Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 3 See the LICENCE file in the repository root for full licence text. 4--}} 5@php 6 use App\Libraries\ApidocRouteHelper; 7 use Knuckles\Camel\Output\OutputEndpointData; 8 9 $baseUrl = $GLOBALS['cfg']['app']['url']; 10 $wikiUrl = wiki_url('Bot_account', null, false); 11 12 $defaultHeaders = [ 13 'Accept' => 'application/json', 14 'Content-Type' => 'application/x-www-form-urlencoded', 15 ]; 16@endphp 17 18<h1>Authentication</h1> 19 20<p> 21 Routes marked with the <a class="badge badge-scope badge-scope-oauth" name="scope-oauth">OAuth</a> label require a valid OAuth2 token for access. 22</p> 23 24<p> 25 More information about applications you have registered and granted permissions to can be found <a href="#managing-oauth-applications">here</a>. 26</p> 27 28<p> 29 The API supports the following grant types: 30 <ul> 31 <li><a href="https://oauth.net/2/grant-types/authorization-code/">Authorization Code Grant</a> 32 <li><a href="https://oauth.net/2/grant-types/client-credentials/">Client Credentials Grant</a> 33 </ul> 34</p> 35 36<p> 37 Before you can use the osu!api, you will need to 38 <ol> 39 <li>have registered an OAuth Application. 40 <li> 41 acquire an access token by either: 42 <ul> 43 <li>authorizing users for your application; 44 <li>requesting Client Credentials token. 45 </ul> 46 </ol> 47</p> 48 49 50<h2>Registering an OAuth application</h2> 51 52<p> 53 Before you can get an OAuth token, you will need to register an OAuth application on your <a href="{{ route('account.edit').'#new-oauth-application' }}">account settings page</a>. 54<p> 55 56<p> 57 To register an OAuth application you will need to provide the: 58</p> 59 60<table> 61 <thead> 62 <tr> 63 <th>Name</th> 64 <th>Description</th> 65 </tr> 66 </thead> 67 <tbody> 68 <tr> 69 <td>Application Name</td> 70 <td> 71 This is the name that will be visible to users of your application. The name of your application cannot be changed. 72 </td> 73 </tr> 74 <tr> 75 <td>Application Callback URL</td> 76 <td> 77 The URL in your application where users will be sent after authorization. 78 </td> 79 </tr> 80 </tbody> 81</table> 82 83<p> 84 The <code>Application Callback URL</code> is required when for using <a href="#authorization-code-grant">Authorization Codes</a>. 85 This may be left blank if you are only using <a href="#client-credentials-grant">Client Credentials Grants</a>. 86</p> 87 88<p> 89 Your new OAuth application will have a <code>Client ID</code> and <code>Client Secret</code>; the <code>Client Secret</code> is like a password for your OAuth application, it should be kept private and <strong>do not share it with anyone else</strong>. 90</p> 91 92 93<h2>Authorization Code Grant</h2> 94 95<p> 96 The flow to authorize users for your application is: 97 <ol> 98 <li>Requesting authorization from users 99 <li>Users are redirected back to your site 100 <li>Your application accesses the API with the user's access token 101 </ol> 102</p> 103 104<aside class="notice"> 105Restricted users can grant authorization like anyone else. If your client should not support restricted users, it can check <code>is_restricted</code> from the <a href="#get-own-data">Get Own Data</a> response. 106</aside> 107 108 109<h3>Request authorization from a user</h3> 110 111@php 112 $description = 'To obtain an access token, you must first get an authorization code that is created when a user grants permissions to your application. To request permission from the user, they should be redirected to:'; 113 $uri = route('oauth.authorizations.authorize', null, false); 114 $endpoint = new OutputEndpointData([ 115 'metadata' => ['authenticated' => false, 'description' => $description], 116 'methods' => ['GET'], 117 'httpMethods' => ['GET'], 118 'uri' => $uri, 119 'queryParameters' => [ 120 'client_id' => [ 121 'description' => 'The Client ID you received when you [registered]('.route('account.edit').'#new-oauth-application).', 122 'name' => 'client_id', 123 'required' => true, 124 'type' => 'integer', 125 'example' => 1, 126 ], 127 'redirect_uri' => [ 128 'description' => 'The URL in your application where users will be sent after authorization. This must match the registered Application Callback URL exactly.', 129 'name' => 'redirect_uri', 130 'example' => 'http://localhost:4000', 131 ], 132 'response_type' => [ 133 'description' => 'This should always be `code` when requesting authorization.', 134 'name' => 'response_type', 135 'required' => true, 136 'example' => 'code', 137 ], 138 'scope' => [ 139 'description' => 'A space-delimited string of [scopes](#scopes).', 140 'name' => 'scope', 141 'required' => false, 142 'example' => 'public identify', 143 ], 144 'state' => [ 145 'description' => 'Data that will be returned when a temporary code is issued. It can be used to provide a token for protecting against cross-site request forgery attacks.', 146 'name' => 'state', 147 'required' => false, 148 'example' => 'randomval', 149 ], 150 ], 151 ]); 152@endphp 153@include('docs.endpoint', [ 154 'endpoint' => $endpoint, 155 'showEndpointTitle' => false, 156 'showRequestTitle' => false, 157]) 158 159<h3>User is redirected back to your site</h3> 160 161@php 162 $description = <<<EOT 163 If the user accepts your request, they will be redirected back to your site with a temporary single-use `code` contained in the URL parameter. 164 If a `state` value was provided in the previous request, it will be returned here. 165 166 <aside class="notice"> 167 If you are using `state` as protection against CSRF attacks, your OAuth client is responsible for validating this value. 168 </aside> 169 170 Exchange this `code` for an access token: 171 172 --- 173 174 ### Response Format 175 176 Successful requests will be issued an access token: 177 178 Name | Type | Description 179 --------------|---------|----------------------------- 180 token_type | string | The type of token, this should always be `Bearer`. 181 expires_in | integer | The number of seconds the token will be valid for. 182 access_token | string | The access token. 183 refresh_token | string | The refresh token. 184 EOT; 185 $uri = route('oauth.passport.token', null, false); 186 $endpoint = new OutputEndpointData([ 187 'bodyParameters' => [ 188 'client_id' => [ 189 'description' => 'The client ID of your application.', 190 'name' => 'client_id', 191 'required' => true, 192 'example' => 1, 193 ], 194 'client_secret' => [ 195 'description' => 'The client secret of your application.', 196 'name' => 'client_secret', 197 'required' => true, 198 'example' => 'clientsecret', 199 ], 200 'code' => [ 201 'description' => 'The code you received.', 202 'name' => 'code', 203 'required' => true, 204 'example' => 'receivedcode', 205 ], 206 'grant_type' => [ 207 'description' => 'This must always be `authorization_code`', 208 'name' => 'grant_type', 209 'required' => true, 210 'example' => 'authorization_code', 211 ], 212 'redirect_uri' => [ 213 'description' => 'This must be the same as the one used on authorization request.', 214 'name' => 'redirect_uri', 215 'required' => false, 216 'example' => 'http://localhost:4000', 217 ], 218 ], 219 'boundUri' => $uri, 220 'cleanQueryParameters' => [], 221 'fileParameters' => [], 222 'headers' => $defaultHeaders, 223 'metadata' => ['authenticated' => false, 'description' => $description], 224 'httpMethods' => ['POST'], 225 'queryParameters' => [], 226 'responses' => [ 227 [ 228 'content' => [ 229 'access_token' => 'verylongstring', 230 'expires_in' => 86400, 231 'refresh_token' => 'anotherlongstring', 232 'token_type' => 'Bearer', 233 ], 234 'status' => 200, 235 ], 236 ], 237 'showresponse' => true, 238 'uri' => $uri, 239 'urlParameters' => [], 240 ]); 241@endphp 242@include('docs.endpoint', [ 243 'endpoint' => $endpoint, 244 'showEndpointTitle' => false, 245 'showRequestTitle' => false, 246]) 247 248<h3>Refresh access token</h3> 249 250@php 251 $description = <<<EOT 252 Access token expires after some time as per `expires_in` field. Refresh the token to get new access token without going through authorization process again. 253 254 Use `refresh_token` received during previous access token request: 255 256 --- 257 258 ### Response Format 259 260 Successful requests will be issued an access token and a new refresh token: 261 262 Name | Type | Description 263 --------------|---------|----------------------------- 264 token_type | string | The type of token, this should always be `Bearer`. 265 expires_in | integer | The number of seconds the token will be valid for. 266 access_token | string | The access token. 267 refresh_token | string | The refresh token. 268 EOT; 269 $uri = route('oauth.passport.token', null, false); 270 $endpoint = new OutputEndpointData([ 271 'bodyParameters' => [ 272 'client_id' => [ 273 'description' => 'The Client ID you received when you [registered]('.route('account.edit').'#new-oauth-application).', 274 'name' => 'client_id', 275 'required' => true, 276 'type' => 'integer', 277 'example' => 1, 278 ], 279 'client_secret' => [ 280 'description' => 'The client secret of your application.', 281 'name' => 'client_secret', 282 'required' => true, 283 'type' => 'string', 284 'example' => 'clientsecret', 285 ], 286 'grant_type' => [ 287 'description' => 'This must always be `refresh_token`.', 288 'name' => 'grant_type', 289 'required' => true, 290 'type' => 'string', 291 'example' => 'refresh_token', 292 ], 293 'refresh_token' => [ 294 'description' => 'Value of refresh token received from previous access token request.', 295 'name' => 'refresh_token', 296 'required' => true, 297 'type' => 'string', 298 'example' => 'longstring', 299 ], 300 'scope' => [ 301 'description' => "A space-delimited string of [scopes](#scopes). Specifying fewer scopes than existing access token is allowed but subsequent refresh tokens can't re-add removed scopes. If this isn't specified, existing access token scopes will be used.", 302 'name' => 'scope', 303 'required' => false, 304 'example' => 'public identify', 305 ], 306 ], 307 'boundUri' => $uri, 308 'cleanQueryParameters' => [], 309 'fileParameters' => [], 310 'headers' => $defaultHeaders, 311 'metadata' => ['authenticated' => false, 'description' => $description], 312 'httpMethods' => ['POST'], 313 'queryParameters' => [], 314 'responses' => [ 315 [ 316 'content' => [ 317 'access_token' => 'verylongstring', 318 'expires_in' => 86400, 319 'refresh_token' => 'anotherlongstring', 320 'token_type' => 'Bearer', 321 ], 322 'status' => 200, 323 ], 324 ], 325 'showresponse' => true, 326 'uri' => $uri, 327 'urlParameters' => [], 328 ]); 329@endphp 330@include('docs.endpoint', [ 331 'endpoint' => $endpoint, 332 'showEndpointTitle' => false, 333 'showRequestTitle' => false, 334]) 335 336<h2>Client Credentials Grant</h2> 337 338@php 339 $description = <<<EOT 340 The client credential flow provides a way for developers to get access tokens that do not have associated user permissions; as such, these tokens are considered as guest users. 341 342 Example for requesting Client Credentials token: 343 344 --- 345 346 ### Response Format 347 348 Successful requests will be issued an access token: 349 350 Name | Type | Description 351 --------------|---------|----------------------------- 352 token_type | string | The type of token, this should always be `Bearer`. 353 expires_in | integer | The number of seconds the token will be valid for. 354 access_token | string | The access token. 355 EOT; 356 $uri = route('oauth.passport.token', null, false); 357 $endpoint = new OutputEndpointData([ 358 'bodyParameters' => [ 359 'client_id' => [ 360 'description' => 'The Client ID you received when you [registered]('.route('account.edit').'#new-oauth-application).', 361 'name' => 'client_id', 362 'required' => true, 363 'type' => 'integer', 364 'example' => 1, 365 ], 366 'client_secret' => [ 367 'description' => 'The client secret of your application.', 368 'name' => 'client_secret', 369 'required' => true, 370 'type' => 'string', 371 'example' => 'clientsecret', 372 ], 373 'grant_type' => [ 374 'description' => 'This must always be `client_credentials`.', 375 'name' => 'grant_type', 376 'required' => true, 377 'type' => 'string', 378 'example' => 'client_credentials', 379 ], 380 'scope' => [ 381 'description' => 'Must be `public`; other scopes have no meaningful effect.', 382 'name' => 'scope', 383 'required' => true, 384 'type' => 'string', 385 'example' => 'public', 386 ], 387 ], 388 'boundUri' => $uri, 389 'cleanQueryParameters' => [], 390 'fileParameters' => [], 391 'headers' => $defaultHeaders, 392 'metadata' => ['authenticated' => false, 'description' => $description], 393 'httpMethods' => ['POST'], 394 'queryParameters' => [], 395 'responses' => [ 396 [ 397 'content' => [ 398 'access_token' => 'verylongstring', 399 'expires_in' => 86400, 400 'token_type' => 'Bearer', 401 ], 402 'status' => 200, 403 ], 404 ], 405 'showresponse' => true, 406 'uri' => $uri, 407 'urlParameters' => [], 408 ]); 409@endphp 410@include('docs.endpoint', [ 411 'endpoint' => $endpoint, 412 'showEndpointTitle' => false, 413 'showRequestTitle' => false, 414]) 415 416<h2>Using the access token to access the API</h2> 417 418<p> 419 With the access token, you can make requests to osu!api on behalf of a user. 420</p> 421 422<p> 423 The token should be included in the header of requests to the API. 424</p> 425 426<p> 427 <code>Authorization: Bearer @{{token}}</code> 428</p> 429 430<div class="bash-example"> 431 <pre><code class="language-bash" 432># With shell, you can just pass the correct header with each request 433curl "{{ $GLOBALS['cfg']['app']['url'] }}/api/[version]/[endpoint]" 434 -H "Authorization: Bearer @{{token}}"</code><pre> 435</div> 436 437<div class="javascript-example"> 438 <pre><code class="language-javascript" 439>// This javascript example uses fetch() 440fetch("{{ $GLOBALS['cfg']['app']['url'] }}/api/[version]/[endpoint]", { 441 headers: { 442 Authorization: 'Bearer @{{token}}' 443 } 444});</code></pre> 445</div> 446 447<blockquote><p>Make sure to replace <code>@{{token}}</code> with your OAuth2 token.</p></blockquote> 448 449<aside class="notice"> 450 You must replace <code>@{{token}}</code> with your OAuth2 token. 451</aside> 452 453 454<h2>Resource Owner</h2> 455 456<p> 457 The <code>Resource Owner</code> is the user that a token acts on behalf of. 458</p> 459 460<p> 461 For <a href="#authorization-code-grant">Authorization Code Grant</a> tokens, the Resource Owner is the user authorizing the token. 462</p> 463 464<p> 465 <a href="#client-credentials-grant">Client Credentials Grant</a> tokens do not have a Resource Owner (i.e. is a guest user), unless they have been granted the {{ ApidocRouteHelper::scopeBadge('delegate') }} scope. The Resource Owner of tokens with the {{ ApidocRouteHelper::scopeBadge('delegate') }} scope is the owner of the OAuth Application that was granted the token. 466</p> 467 468<p> 469 Routes marked with <span class='badge badge-scope badge-user'>requires user</span> require the use of tokens that have a Resource Owner. 470</p> 471 472 473<h2>Client Credentials Delegation</h2> 474 475<p> 476 Client Credentials Grant tokens may be allowed to act on behalf of the owner of the OAuth client (delegation) by requesting the {{ ApidocRouteHelper::scopeBadge('delegate') }} scope, in addition to other scopes supporting delegation. 477 When using delegation, scopes that support delegation cannot be used together with scopes that do not support delegation. 478 Delegation is only available to <a href="{{ $wikiUrl }}">Chat Bot</a>s. 479</p> 480 481<p> 482 The following scopes currently support delegation: 483</p> 484 485<table> 486 <thead> 487 <tr> 488 <th>Name</th> 489 </tr> 490 </thead> 491 <tbody> 492 <tr> 493 <td>{{ ApidocRouteHelper::scopeBadge('chat.write') }}</td> 494 </tr> 495 </tbody> 496</table> 497 498<h2>Scopes</h2> 499 500<p> 501 The following scopes are currently supported: 502</p> 503 504@php 505$scopeDescriptions = [ 506 'chat.read' => "Allows read chat messages on a user's behalf.", 507 'chat.write' => "Allows sending chat messages on a user's behalf.", 508 'chat.write_manage' => "Allows joining and leaving chat channels on a user's behalf.", 509 'delegate' => "Allows acting as the owner of a client; only available for [Client Credentials Grant](#client-credentials-grant).", 510 'forum.write' => "Allows creating and editing forum posts on a user's behalf.", 511 'friends.read' => 'Allows reading of the user\'s friend list.', 512 'identify' => 'Allows reading of the public profile of the user (`/me`).', 513 'public' => 'Allows reading of publicly available data on behalf of the user.', 514]; 515@endphp 516 517<table> 518 <thead> 519 <tr> 520 <th>Name</th> 521 <th>Description</th> 522 </tr> 523 </thead> 524 <tbody> 525 @foreach ($scopeDescriptions as $scope => $description) 526 <tr> 527 <td> 528 <a class="badge badge-scope badge-scope-{{ $scope }}" name="scope-{{ $scope }}">{{ $scope }}</a> 529 </td> 530 <td>{!! markdown_plain($description) !!}</td> 531 </tr> 532 @endforeach 533 <tr> 534 </tr> 535 </tbody> 536</table> 537 538<p> 539 <code>identify</code> is the default scope for the <a href="#authorization-code-grant">Authorization Code Grant</a> and always implicitly provided. The <a href="#client-credentials-grant">Client Credentials Grant</a> does not currently have any default scopes. 540</p> 541 542<p> 543 Routes marked with <a class="badge badge-scope badge-scope-lazer" name="scope-lazer">lazer</a> are intended for use by the <a href="https://github.com/ppy/osu">osu!lazer</a> client and not currently available for use with Authorization Code or Client Credentials grants. 544</p> 545 546<p> 547 Using the {{ ApidocRouteHelper::scopeBadge('chat.write') }} scope requires either 548 <ul> 549 <li>a <a href="{{ $wikiUrl }}">Chat Bot</a> account to send messages on behalf of other users. 550 <li>Authorization code grant where the user is the same as the client's owner (send as yourself). 551 </ul> 552</p> 553 554 555<h2>Managing OAuth applications</h2> 556 557<p> 558 Your <a href="{{ route('account.edit').'#oauth' }}">account settings</a> page will show your registered OAuth applications, and all the OAuth applications you have granted permissions to. 559</p> 560 561<h3>Reset Client Secret</h3> 562 563<p> 564 You can generate a new <code>Client Secret</code> by choosing to "Reset client secret", however, this will disable all access tokens issued for the application. 565</p>