Describing the problem
I was struggeling the last few days to figure out how to use apikey security in openapi, swagger, connexion for role based token authentication. The following OpenAPI 3.0 endpoint definition:
/lab/samples/list:
get:
tags:
- lab
summary: get a list of all registered samples
operationId: list_samples
responses:
"200":
description: successfully returned all available samples and their notification status
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Sample-For-Lab'
x-content-type: application/json
"400":
description: invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/inline_response'
security:
- bearerAuth: ['labuser']
with the corresponding security definition
securitySchemes:
bearerAuth:
type: apiKey
name: Authorization
in: header
x-apikeyInfoFunc: swagger_server.controllers.authorization_controller.check_bearerAuth
So far so good. I built the corresponding server stubs using swagger-codegen, which follow the connexion security model and provide two fields api_key i.e. the bearer token and 'required_scopes' i.e. which should contain 'labuser'. When accessing the endpoint, the controller function is called:
def check_adminuserAuth(api_key, required_scopes):
return {'sample_key' : 'sample_value}
While the bearer token is properly passed, required_scopes is None. So there's no way of actually validating if credentials and permissions shown in the provided token actually match the endpoint's required scope of labuser in the authorization controller. I thought about handling validation in the called endpoints list_systemusers() but the token is no passed on by connexion.
Not supported in OpenAPI 3.0
After doing some digging, I found out that OpenAPI 3.0 provides apiKey validation on a global API level (i.e. authenticated or not), but does not offer support for individual scopes per endpoint. If you want individual scopes, you need to switch to OAuth security. However support for security scopes through apiKey security is coming in OpenAPI 3.1
Workaround
So for now the only way of making bearer token security with individual scopes work, is to actually define a security scheme for every scope e.g.
securitySchemes:
adminuserAuth:
type: apiKey
description: Provide your bearer token in the format **Bearer <token>**
name: Authorization
in: header
x-apikeyInfoFunc: swagger_server.controllers.authorization_controller.check_adminuserAuth
statsuserAuth:
type: apiKey
description: Provide your bearer token in the format **Bearer <token>**
name: Authorization
in: header
x-apikeyInfoFunc: swagger_server.controllers.authorization_controller.check_statsuserAuth
labuserAuth:
type: apiKey
description: Provide your bearer token in the format **Bearer <token>**
name: Authorization
in: header
x-apikeyInfoFunc: swagger_server.controllers.authorization_controller.check_labuserAuth
and on the path definition then add your required security authentication schemes
security:
- labuserAuth: []
- adminuserAuth: []
x-openapi-router-controller: swagger_server.controllers.lab_controller
Now I know by which authorization controller method is called the required scope a user needs to show and therefore can validate it against the ones shown in the token.
Related
Running latest (and now old due to the switchover to flask REST-X) flask RESTPlus using the authorization functionality for the swagger interface with a Bearer token as follows:
authorizations = {
'apikey': {
'type': 'apiKey',
'in': 'header',
'name': 'Bearer '
}
But although the "Authorise" box comes up in the swagger interface, and I can put a token in there, it doesn't get added to the requests coming out or the curl format that swagger provides, so we can see clearly it's not being picked up. What's going on here and how do I fix it?
Make sure the code also has annotations that would add security to individual operations or globally. This is needed to actually attach the Authorization header to operations.
In other words, the generated OpenAPI definition should contain the following.
If using OpenAPI 2.0:
swagger: '2.0'
securityDefinitions:
apikey:
type: apiKey
in: header
name: Authorization
security:
- apiKey: []
If using OpenAPI 3.0:
openapi: 3.0.0
components:
securitySchemes:
apikey:
type: apiKey
in: header
name: Authorization
# or using OAS3 Bearer auth scheme
# apiKey:
# type: http
# scheme: bearer
security:
- apiKey: []
In swagger With api 3 version I have login described as :
/login:
post:
tags:
- user login
summary: User login
responses:
'200':
description: Successful login
content:
application/json:
schema:
$ref: '#/components/schemas/UserLogin'
'400':
description: Invalid login
operationId: postLogin
requestBody:
description: Login user fields
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
email:
type: string
default: admin#site.com
password:
type: string
default: 111111
required:
- email
- password
and if login is successfull amonth returned data I have access_token field.
With schema described as :
components:
schemas:
UserLogin:
properties:
access_token:
type: string
user:
$ref: '#/components/schemas/UserLogin'
token_type:
type: string
user_avatar_path:
type: string
usersGroups:
type: array
expires_in:
type: integer
I need to use this access_token value for next post requests to get access to authorized pages.
How can I do this in swagger. Seems I can not copypaste access_token value from login requeest for
my post requests, asthey do not access_token parameter ?
MODIFIED BLOCK:
I found how to copypaste access_token from login : https://prnt.sc/rs3ck6
/personal/profile url to read profile data has no in params in qiuery, so I defined access_token as :
securitySchemes:
ApiKeyAuth: # arbitrary name for the security scheme
type: apiKey
in: header # I suppose it must be "header"
name: access_token
But how to add access_token to header of my /personal/profile ?
I see : https://prnt.sc/rs3i90
and clciking on button with Lock sign I see modal dialog “Available authorizations”, which is empty.
I suppose I have to select access_token from it?
Thanks!
What is in my mind is API Keys authentication. This will reduce times you need to copy paste your access_token.
Assume you need to pass access_token via query string on each POST request, then you could set up authentication globally in swagger like this:
components:
securitySchemes:
ApiKeyAuth:
type: apiKey
name: access_token
in: header
security:
- ApiKeyAuth: []
or, if you are using Bearer authentication:
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
security:
- bearerAuth: []
After you get an access_token from /login call, you could click Authorize button under API information, and set it there. Then all the subsequent calls made via Swagger UI should automatically have access_token in the query string like this:
https://petstore.swagger.io/v2/login?access_token=1234
In Google Cloud Endpoints, I am unable to have authentication occur in an "or" scenario, where, for example, I want to allow access to a path by means of an api_key "or" oauth. Please see https://swagger.io/docs/specification/2-0/authentication/ for more info.
DOES WORK (single defintion for API key)
/api/query:
get:
operationId: queryget
responses:
'200':
description: query success
security:
- api_key: []
DOES WORK (single definition for oauth)
/api/query:
get:
operationId: queryget
responses:
'200':
description: query success
security:
- oauth: []
DOES WORK ("AND" definition where both must be included in auth)
/api/query:
get:
operationId: queryget
responses:
'200':
description: query success
security:
- oauth: []
api_key: []
DOES NOT WORK ("OR" definition)
/api/query:
get:
operationId: queryget
responses:
'200':
description: query success
security:
- oauth: []
- api_key: []
To be more specific when deploying my api to Google Cloud Endpoints, I receive the following warning
Operation 'get' in path '/api/query': Operation does not require an API key; callers may invoke the method without specifying an associated API-consuming project.
And, when trying to call the api with an API key, I receive the following error, as if it were expecting the OAUTH JWT token (maybe because it was the first security definition in the list for that route)
{ "code": 16, "message": "JWT validation failed: Missing or invalid credentials", "details": [ { "#type": "type.googleapis.com/google.rpc.DebugInfo", "stackEntries": [], "detail": "auth" } ]}'
This feature is unsupported, unfortunately. You can only use AND. This is now documented in Unsupported OpenAPI Features.
i get this error on swagger editor when using the auth0
Schema error at securityDefinitions.auth0
is not exactly one from <#/definitions/basicAuthenticationSecurity>,<#/definitions/apiKeySecurity>,<#/definitions/oauth2ImplicitSecurity>,<#/definitions/oauth2PasswordSecurity>,<#/definitions/oauth2ApplicationSecurity>,<#/definitions/oauth2AccessCodeSecurity>
Jump to line 67
where my .yaml file is like:
securityDefinitions:
auth0:
type: oauth2
authorizationUrl: https://domain.auth0.com/authorize
flow: implicit
tokenName: id_token
scopes:
openid: Grant access to user
apiKey:
type: apiKey
name: api-key
in: query
apiKey1:
type: apiKey
name: api-key
in: header
what am i missing?
Remove tokenName - it's not a supported field.
Check the specification to see which fields are allowed in securityDefinitions:
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#securitySchemeObject
Is it possible to have same path appear more than once in one API spec which is being rendered by Swagger-UI?
Should I create separate api specs and load two instances of Swagger-UI? What is the best way to handle this?
For ex. I have endpoint called /oauth/token which I want to document with one set of parameters for OAuth Authorization Code flow and the same endpoint /oauth/token documents for client_credentials flow with different set of parameters.
/oauth/token:
post:
summary: token endpoint for authorization_code flow
parameters:
- name: code
type: string
description: Required for Authorization Code Flow
in: formData
required: true
- name: grant_type
type: string
description: Grant Type should be specified as authorization_code
in: formData
required: true
default: authorization_code
enum:
- authorization_code
- client_credentials
- name: client_id
type: string
description: Consumer Key
in: formData
required: true
- name: client_secret
type: string
description: Consumer Secret
in: formData
required: true
- name: endOtherSessions
in: formData
type: boolean
required: false
default: false
description: Optional parameter. Default is false - do not allow concurrent login. Send true to end any other user sessions.
tags:
- OAuth2 Authorization Code
Same endpoint for client_credentials flow
/oauth/token2:
post:
summary: token for client credentials
parameters:
- name: grant_type
type: string
description: Grant Type should be specified as client_credentials
in: formData
required: true
default: client_credentials
enum:
- authorization_code
- client_credentials
- name: client_id
type: string
description: Consumer Key
in: formData
required: true
- name: client_secret
type: string
description: Consumer Secret
in: formData
required: true
tags:
- OAuth2 Client Credentials
Since the question is about OAuth2 rather than a single endpoint with different parameters, then the solution is actually different.
Swagger has a specific way to document authorization methods, including the 4 common OAuth2 flows.
These are described using the Security Definitions Object which is located at the top Swagger object.
Within it, you can define one or more OAuth2 flows. The spec itself provides a sample for the implicit flow, but others follow a similar structure. The difference is by which fields are provided, namely authorizationUrl and tokenUrl (depending on the flow type).
Once you have that, you can specify which security schemes are required. You can specify it for all the operations at the Swagger object or at the Operation level.
The spec allows you to define that a set of security methods are required together, or that the user can choose between given sets.