Auth addition to flask-restx
Hey everyone, I was wondering if adding auth around endpoints is on the road map? Right now I see a lot of examples to just add a custom decorator functions to check api keys and other forms of auth. It seems pretty simplistic and I was wondering if flask-restx could provide a built in option?
Is this already a capability of flask-restx?
Solution if this does not exist: Add a built in decorator that you can add to each endpoint similar to @api.response or @api.expect. Then add a option in the Api class for the authorization key or something.
I would like your feedback on this idea and if I am thinking about this wrong.
Hello,
You can already pass a decorator to the Api constructor that will be passed to each endpoint.
This way, you can use your preferred third-party library/framework for the authentication part.
The Api constructor also provides a parameter that allow you to define custom fields/headers that should be forwarded to your endpoints when using the swagger-ui.
The parameters you might be interested in are authorizations and decorators as documented here.
Is there any thought to solving https://github.com/noirbizarre/flask-restplus/issues/680 now that the corresponding issue in Swagger-UI (https://github.com/swagger-api/swagger-ui/issues/5348) has been solved?
The docs still say: "Using PKCE instead of Implicit Flow depends on https://github.com/swagger-api/swagger-ui/issues/5348"
Any updates on this? I'll take a look through the code, but hoping someone has a straightforward answer
no at moment, the swagger version on it already supports the PKCE, but we can´t set the variables in settings.
I did a workaround extending the swagger template (creating a template directory and create a file with the same name) and setting the PKCE required inputs,
example of my custom ui.initOAuth
ui.initOAuth({
clientId: "{{ config.SWAGGER_UI_OAUTH_CLIENT_ID }}",
realm: "{{ config.SWAGGER_UI_OAUTH_REALM }}",
appName: "{{ config.SWAGGER_UI_OAUTH_APP_NAME }}",
// Auth0 extra query strings for PCKE
additionalQueryStringParams: {
audience: "{{ config.SWAGGER_UI_OAUTH_APP_AUDIENCE }}", # Custom settings created by me
response_mode: 'query' # fixed value works for me, but its depend of you OAuth2 config
},
usePkceWithAuthorizationCodeGrant: true // This set to true
})
maybe could help you @its-miller-time
I will work in a PR :)
Is there any update on this? @jrnp97 thanks for the headsup on the override! It works a treat in the meantime
@jrnp97 @euan-cowie Could you please explain a bit more about what is the workaround explained in your previous comments?
Right now, I was trying the below in flask-restx
authorizations = {
'oauth2': {
'type': 'oauth2',
'flow': 'accessCode',
'clientId': "some-client-id",
'clientSecret': "some-client-secret",
'authorizationUrl': 'https://<auth-server-host-name>/oauth2/<auth-server-id>/v1/authorize?code_challenge=nOoaE86Z__cUR1Js04dFwzqD6cqvmnzlQ4xR9KNZDIU&code_challenge_method=S256&response_mode=query',
'tokenUrl': 'https://<auth-server-host-name>/oauth2/<auth-server-id>/v1/token?code_verifier=209a9d4d49f39b64f2c908a848bd30ddcb9db812caf89017f8cbe8b9ee493e1e',
'scopes': {
'profile': 'Get identity',
}
}
}
api_v1_blueprint = Blueprint("api", __name__, url_prefix="/api/v1")
api = Api(api_v1_blueprint, authorizations=authorizations)
This is not working for me. I am getting the below error. Would really appreciate any clues on why this is happening. One guess I have is, in my okta-auth-server the application type that I have is "Web applicaiton", and as per this post, it should be "single page applicaiton" instead. Did not try this one yet due to lack of permission to change that.
Auth error Error: Unauthorized, error: invalid_client, description: Client authentication failed. Either the client or the client credentials are invalid.
UPDATEs: After trying out a couple of things, found the complete configs for "authcode + pkce" flow using "swagger UI" and "flask-restx". (I was using okta auth server, should be same for others)
-
While creating an application in auth-server, we need to make sure to choose the application type as "Single Page Applicaiton". If we select any other application type, okta has configs that says invalid client credentials even if we send empty client_secret.
-
To redirect the received auth_code from 1st authorization request and send that to accessToken request. This has to handled by the client. In our case, client is swagger, swagger has "oauth2-redirect.html", we have to see what is the redirect uri mentioned in the 1st request for authorization. And then make sure that "oauth2-redirect.html" file is rendered on that redirect uri. This we can taken care automatically if we have set
app.config.SWAGGER_UI_OAUTH_CLIENT_ID = 'MyClientId'in the app, because mentioning redirect url is done using and if block with check on presence of above config var in swagger-ui.html file. or we can do manually if not working. -
Now, about the workaround for using PKCE query params automatically in our requests to authserver. Thanks, @jrnp97 for sharing the workaround. Created a template folder as per the structure of the project, and placed a copy of swagger-ui.html file there. Configured the
ui.initOauthblock by addingusePkceWithAuthorizationCodeGrantlike below
{% if config.SWAGGER_UI_OAUTH_CLIENT_ID -%}
ui.initOAuth({
clientId: "{{ config.SWAGGER_UI_OAUTH_CLIENT_ID }}",
realm: "{{ config.SWAGGER_UI_OAUTH_REALM }}",
appName: "{{ config.SWAGGER_UI_OAUTH_APP_NAME }}",
additionalQueryStringParams: {
response_mode: 'query'
},
usePkceWithAuthorizationCodeGrant: true // This set to true
})
Again you can see that, its inside an if block, so make sure to set app.config.SWAGGER_UI_OAUTH_CLIENT_ID = clientId while setting app config properties.
While making sure that the above things are setup. I setup the authorizations as suggested in official doc https://flask-restx.readthedocs.io/en/latest/swagger.html
app = Flask(__name__)
app.config.SWAGGER_UI_OAUTH_CLIENT_ID = clientId
authorizations = {
'oauth2': {
'type': 'oauth2',
'flow': 'accessCode',
'clientId': "<clientId>",
'authorizationUrl': '<auth_url>',
'tokenUrl': 'token_url',
'scopes': {
'openid': 'Get ID token',
}
}
}
api_v1_blueprint = Blueprint("api", __name__, url_prefix="/api/v1")
api = Api(api_v1_blueprint, authorizations=authorizations)