Skip to content

Commit

Permalink
Merge pull request #19 from dedoc/feature/issue-11
Browse files Browse the repository at this point in the history
Added security docs of the API
  • Loading branch information
romalytvynenko authored Sep 20, 2022
2 parents fb6fc03 + 37ab2e2 commit d06deab
Show file tree
Hide file tree
Showing 13 changed files with 401 additions and 17 deletions.
8 changes: 4 additions & 4 deletions docs/usage/access.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
title: Restricting access to docs
weight: 4
title: Docs authorization
weight: 5
---

The access to docs in non-production environment is enabled by default.
Scramble exposes docs at the `/docs/api` URI. By default, you will only be able to access this route in the `local` environment.

If you need to allow access to docs in production environment, implement gate called `viewApiDocs`:
If you need to allow access to docs in `production` environment, implement gate called `viewApiDocs`:

```php
Gate::define('viewApiDocs', function (User $user) {
Expand Down
61 changes: 61 additions & 0 deletions docs/usage/security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
title: Security
weight: 4
---

Most likely, your API is protected by some sort of the auth. OpenAPI have plenty of ways to describe the API (see full documentation of spec here https://spec.openapis.org/oas/v3.1.0#security-scheme-object).

## Adding security scheme
Scramble allows you to document how your API is secured. To document this, you can use `Scramble::extendOpenApi` method and add security information to OpenAPI document using `secure` method.

You should call `extendOpenApi` in `register` method of some of your service providers. This method accepts a callback that accepts OpenAPI document as a first argument.

`secure` method on `OpenApi` object accepts security scheme as an argument. It makes the security scheme default for all endpoints.

```php
namespace App\Providers;

use Dedoc\Scramble\Support\Generator\OpenApi;
use Dedoc\Scramble\Support\Generator\SecurityScheme;
use Illuminate\Support\ServiceProvider;

class DocsServiceProvider extends ServiceProvider
{
public function register()
{
Scramble::extendOpenApi(function (OpenApi $openApi) {
$openApi->secure(
SecurityScheme::apiKey('query', 'api_token')
);
});
}
}
```

## Examples
Here are some common examples of the security schemes object you may have in your API. For full list of available methods check implementation of `SecurityScheme` class.

### API Key
```php
SecurityScheme::apiKey('query', 'api_token');
```

### Basic HTTP
```php
SecurityScheme::http('basic');
```

### JWT
```php
SecurityScheme::http('bearer', 'JWT');
```

### Oauth2
```php
SecurityScheme::oauth2()
->flow('implicit', function (OAuthFlow $flow) {
$flow
->authorizationUrl('https://example.com/api/oauth/dialog')
->addScope('write:pets', 'modify pets in your account');
});
```
2 changes: 1 addition & 1 deletion src/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public function __invoke()
->toArray();

if (isset(Scramble::$openApiExtender)) {
$openApi = (Scramble::$openApiExtender)($openApi);
(Scramble::$openApiExtender)($openApi);
}

return $openApi->toArray();
Expand Down
12 changes: 12 additions & 0 deletions src/Support/Generator/OpenApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ public function setComponents(Components $components)
return $this;
}

public function secure(SecurityScheme $securityScheme)
{
$securityScheme->default();

$this->components->addSecurityScheme($securityScheme->schemeName, $securityScheme);
if ($securityScheme->default) {
$this->defaultSecurity(new Security($securityScheme->schemeName));
}

return $this;
}

public function setInfo(InfoObject $info)
{
$this->info = $info;
Expand Down
57 changes: 45 additions & 12 deletions src/Support/Generator/SecurityScheme.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,76 @@

namespace Dedoc\Scramble\Support\Generator;

use Dedoc\Scramble\Support\Generator\SecuritySchemes\ApiKeySecurityScheme;
use Dedoc\Scramble\Support\Generator\SecuritySchemes\HttpSecurityScheme;
use Dedoc\Scramble\Support\Generator\SecuritySchemes\Oauth2SecurityScheme;
use Dedoc\Scramble\Support\Generator\SecuritySchemes\OpenIdConnectUrlSecurityScheme;

class SecurityScheme
{
private string $type;
public string $type;

private string $name;
public string $description = '';

private string $in;
public string $schemeName = 'scheme';

private string $description = '';
public bool $default = false;

private function __construct(string $type)
public function __construct(string $type)
{
$this->type = $type;
}

public static function apiKey(string $in, string $name)
{
$scheme = new self('apiKey');
$scheme->in = $in;
$scheme->name = $name;
return (new ApiKeySecurityScheme($in, $name))->as('apiKey');
}

public static function http(string $scheme, string $bearerFormat = '')
{
return (new HttpSecurityScheme($scheme, $bearerFormat))->as('http');
}

public static function oauth2()
{
return (new Oauth2SecurityScheme)->as('oauth2');
}

public static function openIdConnect(string $openIdConnectUrl)
{
return (new OpenIdConnectUrlSecurityScheme($openIdConnectUrl))->as('openIdConnect');
}

public static function mutualTLS()
{
return (new static('mutualTLS'))->as('mutualTLS');
}

public function as(string $schemeName): self
{
$this->schemeName = $schemeName;

return $scheme;
return $this;
}

public function setDescription(string $description): SecurityScheme
public function setDescription(string $description): self
{
$this->description = $description;

return $this;
}

public function default(): self
{
$this->default = true;

return $this;
}

public function toArray()
{
return array_filter([
'type' => $this->type,
'in' => $this->in,
'name' => $this->name,
'description' => $this->description,
]);
}
Expand Down
28 changes: 28 additions & 0 deletions src/Support/Generator/SecuritySchemes/ApiKeySecurityScheme.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Dedoc\Scramble\Support\Generator\SecuritySchemes;

use Dedoc\Scramble\Support\Generator\SecurityScheme;

class ApiKeySecurityScheme extends SecurityScheme
{
public string $name;

public string $in;

public function __construct(string $in, string $name)
{
parent::__construct('apiKey');

$this->in = $in;
$this->name = $name;
}

public function toArray()
{
return array_merge(parent::toArray(), [
'in' => $this->in,
'name' => $this->name,
]);
}
}
28 changes: 28 additions & 0 deletions src/Support/Generator/SecuritySchemes/HttpSecurityScheme.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Dedoc\Scramble\Support\Generator\SecuritySchemes;

use Dedoc\Scramble\Support\Generator\SecurityScheme;

class HttpSecurityScheme extends SecurityScheme
{
public string $scheme;

public string $bearerFormat = '';

public function __construct(string $scheme, string $bearerFormat = '')
{
parent::__construct('http');

$this->scheme = $scheme;
$this->bearerFormat = $bearerFormat;
}

public function toArray()
{
return array_merge(parent::toArray(), [
'scheme' => $this->scheme,
'bearerFormat' => $this->bearerFormat,
]);
}
}
53 changes: 53 additions & 0 deletions src/Support/Generator/SecuritySchemes/OAuthFlow.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

namespace Dedoc\Scramble\Support\Generator\SecuritySchemes;

class OAuthFlow
{
public string $authorizationUrl = '';

public string $tokenUrl = '';

public string $refreshUrl = '';

/** @var array<string, string> */
public array $scopes = [];

public function authorizationUrl(string $authorizationUrl): OAuthFlow
{
$this->authorizationUrl = $authorizationUrl;

return $this;
}

public function tokenUrl(string $tokenUrl): OAuthFlow
{
$this->tokenUrl = $tokenUrl;

return $this;
}

public function refreshUrl(string $refreshUrl): OAuthFlow
{
$this->refreshUrl = $refreshUrl;

return $this;
}

public function addScope(string $name, string $description = '')
{
$this->scopes[$name] = $description;

return $this;
}

public function toArray()
{
return array_filter([
'authorizationUrl' => $this->authorizationUrl,
'tokenUrl' => $this->tokenUrl,
'refreshUrl' => $this->refreshUrl,
'scopes' => $this->scopes,
]);
}
}
55 changes: 55 additions & 0 deletions src/Support/Generator/SecuritySchemes/OAuthFlows.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace Dedoc\Scramble\Support\Generator\SecuritySchemes;

class OAuthFlows
{
public ?OAuthFlow $implicit = null;

public ?OAuthFlow $password = null;

public ?OAuthFlow $clientCredentials = null;

public ?OAuthFlow $authorizationCode = null;

public function implicit(?OAuthFlow $flow): OAuthFlows
{
$this->implicit = $flow;

return $this;
}

public function password(?OAuthFlow $flow): OAuthFlows
{
$this->password = $flow;

return $this;
}

public function clientCredentials(?OAuthFlow $flow): OAuthFlows
{
$this->clientCredentials = $flow;

return $this;
}

public function authorizationCode(?OAuthFlow $flow): OAuthFlows
{
$this->authorizationCode = $flow;

return $this;
}

public function toArray()
{
return array_map(
fn ($f) => $f->toArray(),
array_filter([
'implicit' => $this->implicit,
'password' => $this->password,
'clientCredentials' => $this->clientCredentials,
'authorizationCode' => $this->authorizationCode,
])
);
}
}
Loading

0 comments on commit d06deab

Please sign in to comment.