In my API documentation, I would like to define the security necessary for each API endpoint. The project has defined roles and permissions that determine which users can access the APIs. What is the best way in Swagger to document this information? Is there a best practice or recommendation on how to show this detail?
This what I tried out using securityDefinitions and a self-defined variable for the roles, but that information (x-role-names) didn't get copied over into the documentation when I ran it through swagger2markup or using swagger-ui.
"securityDefinitions": {
"baseUserSecurity": {
"type": "basic",
"x-role-names": "test"
}
}
What's the best way to document the role and permission information per endpoint?
If your API uses oAuth authentication, you can use scopes for this. There is no standard way to represent roles in Swagger/OpenApi against basic authentication, so you are left using vendor-extensions (which the tools such as Swagger-UI or swagger2markup have no way of interpreting, as you have found), or including the information as text in summary or description properties.
You could define multiple securityDefinitions all of type basic and use one per role but this is a bit of a hack.
See also this issue https://github.com/OAI/OpenAPI-Specification/issues/1366 for a proposal to widen the use of scopes to other security schemes.
Related
I'm trying to wrap my head around scopes for this scenario.
I have a SPA client and an API. The client only communicates with this API and the API has no other clients communicating with it.
The application has two access levels, say a user and an admin (the SPA may block some routes for the user and the API may block some endpoints).
The roles are administered with AD-groups and mapped to the roles claim.
So what role does scopes play in this scenario? I do all authorization based on the roles claim. But I still need to specify a scope, so I have a API://[clientid]/all scope. Could someone help me make sense of all this?
Scopes are fixed at design time. They are high level privileges that indicate an area of data and what you can do with that data. These are often used as sanity checks, eg to prevent tokens for a valid user but wrong app being used to call an API.
orders_read
Claims are dynamic values looked up at runtime, and tend to have different values for different users. Pretty much all real world authorization is based on claims:
role = supervisor
company_id = 407
So in your case just define a scope or two, but keep them high level and easy to manage. Your claims based authorization (using roles) seems fine.
FURTHER INFO
At Curity we have a couple of good docs that explain the science of designing authorization based on OAuth standards:
Scope Best Practices
Claims Best Practices
I'm aware of how OAuth2 and OIDC can use custom scopes and token introspection to secure an URL like this:
/users/me/documents
I can give this URL the documents:view scope and when receiving the token from the authenticated user, I can ask the authorization server if this user has the correct permissions. Then I can use the preferred_username claim or similar to see who /me actually is.
But what if I have a resource which is accessible by multiple users? Let's say a user has documents but they can be viewed by his direct manager. To retrieve the employee's documents as a manager, I'd need to have an url like this:
/users/${userId}/documents
How could I enforce it in a way that only the resource owner and direct manager can view this resource? I don't want everyone to access everyone's documents by knowing the userId. I could grant access as a whole to all users with the manager role, but that's not specific enough.
I'm aware there's the UMA extension where users can grant access to resources on his behalf to other users, but it's not the user who grants permission. It's the system who states in this case that managers can access their employees documents.
Would it make sense to write a custom policy which extracts the ${userId} and performs the check? Or should this not be done by the authorization server at all and be done by the resource server instead? Perhaps a different approach to reach the same goal?
Finer grained authorization like this is done with claims rather than scopes. There may be business rules around which docs a user can see, eg:
A user can access their own docs
An admin has view access to all docs
A manager can view docs for people they manage
In an access token this might be represented by these claims:
userId
role
Claims are often domain specific like this and the preferred option is to add them to tokens during token issuance. At Curity we have some good resources on this topic:
Claims Best Practices
CLAIMS AND AUTHORIZATION
The Authorization Server issues access tokens and then APIs (resource servers) verify the access token and use the token data to apply authorization rules (which are often domain specific) on every single request.
Claims are often used when dynamic behaviour is needed - they are runtime values that derive from the user identity, whereas scopes are fixed design time values. In your example an API might also need to vary SQL to retrieve documents based on the user identity.
There are more complex variations on this theme, such as an API calling a system such as Open Policy Agent, so that documents returned are determined by rules configured by a security administrator. That policy would still involve using claims from the access token though.
EXAMPLE CODE
If it helps, here is some sample code of mine that show the type of approach when enforcing domain specific authorization rules. Typically you need to filter collections and check access to individual items.
I have an application which provides authenticated users with views into data about various objects in a database. There's another application in our ecosystem that provides different views into some of the same objects, using its own permission model. We trust that other application's permission model, and would like to allow them to issue access tokens to users who haven't been authenticated through our application's usual method, so those users can only view specific objects that the other application has verified they have access to.
Rather than coming up with our own spec for the communication between these two applications, I was wondering if there's already a standard approach available via something like OpenID Connect. OIDC seems to handle most of the gnarly details we'd have to consider in a case like this, but the one aspect where it doesn't seem to fit is that its access tokens seem to be general-purpose, rather than calling out a specific object that the user has access to. It says "Here's a user who can access your application", but not "Here's a user who can access Item 123".
Is there a standard for using an access token to grant access to a specific item, preferably using OAuth 2 and/or OpenID Connect? Am I correct in assuming that using an item's ID as a scope on the access token would be an inappropriate use of OAuth scopes?
I've always found the best design for most real world apps to be like this:
OAuth 2.0 based tech identifies the user
You then lookup user details at an application level to enforce authorization
OAuth 2.0 scopes etc cannot handle things like this:
You don't have access to account 123
You don't have access to region US
So I tend to look them up from the user id in the token after login. This tends also to be much easier to extend, if for example the items and user rights in the external app grows over time.
For more concrete info see my write up on API Claims Caching.
Also here is an example of the coded algorithm in a Rest API, resulting in a claims object that can be injected into logic classes.
In your case the custom claims provider would be the external app, and you could query claims from it, for data that does not really fit well into OAuth tokens.
Just my thoughts - not sure if it will fully work for you - but I've found this to be quite an adaptable solution, which often puts responsibilities in the right places.
As the creator of an API ecosystem with OAuth, you want to allow access to your APIs via scopes that can be attached to an access token. My questions are thus:
How do YOU define a scope?
Would you expect any scopes you define to provide access only to resources/methods within a single API, or should a scope include access across multiple APIs?
How likely are you to define scopes in an API definition file like Swagger/OAS or RAML vs. within some sort of API gateway tool?
Should scopes be defined outside of the context of a specific API as part of a separate OAuth management tool, alongside things like IdP registration and client application creation?
I realize there may be several possible answers and perspectives on this - that is exactly what I am looking for.
Thanks for your help!
My OAuth2 server has the ability to create arbitrary scopes. Scopes are similar to 'roles', they describe groups of functionality.
The OAuth2 server is essentially unaware of what theses scopes are. API Resource servers receive Bearer tokens, and based on the bearer tokens find out what scopes are associated with it, and make decisions on what a user can and cannot do with that scope, but to the OAuth2 server, they are opaque strings.
We don't use swagger/RAML.
I'm working on a service that provides smart (hopefully) integration of different services supporting OAuth 2.0. The focus of our tool is on team work flow improvement, so we're combining Slack, GitHub, Asana (issue tracker), Cezanne (hr tool), etc.
We have ui and backend that work with all those tools (user is authorised to all of them, so I have required access and refresh tokens). We need to be able to hide different parts of the ui depending on person's role in a specific tool. Let's take GitHub as an example. The user can be a repository owner, contributor, company owner (for business account), etc, so those user might need different ui based on their rights.
Originally I was hesitant implementing authorisation on my own (another custom authorisation system is the last thing this world needs), I wanted to take advantage of other services' authorisation mechanisms and just create a lightweight wrapper around them. It seemed like a reasonable idea at first, but I can't figure out how to implement it and Google doesn't give valuable advice which means: 99.99% I'm trying to do something stupid, 00.01% I'm trying to do something rare/innovative.
I hoped to take advantage of OAuth 2.0 but it doesn't seem to support what we need. The closest thing is scopes but it doesn't look very relevant to our scenario.
The only idea I have for now is to create our own authorisation system and integrate other services using kind of reverse engineering. So I would request user's GitHub account details using API and apply him roles in our system appropriately: Owner for repository A, contributor for repository B, owner of company C, etc. I will have to reverse-engineer the permission for each role (i. e. repository owner can not change company name). And we would have to keep user roles for each service: so instead of typical Admin/User/Manager/etc. we will get: OwnerOfGitHubRepository (for repositoryA), ManagerOfAsanaTeam (for team B), etc.
It would be awesome if OAuth 2.0 services had an endpoint that would return the permissions available for a current user.
I'm not a security engineer, so I might be missing something obvious. So wanted to ask you guys for advice before investing into the implementation mentioned above.
The word, "authorization", is used in two different contexts.
In one context, authorization means "who has what permissions". Solutions for this authorization is "identity management".
In the other context, authorization means "who grants what permissions to whom". Solutions for this authorization is "OAuth".
In some cases, you may have to handle these two authorizations simultaneously. See this question and this answer for details.
You tagged your question with identityserver4.
This Issue for identityserver3 from last year may interest you.
But I'm afraid most providers don't support this oauth2 profile (yet).
UMA seems to be an oauth2 way to enable fine grained authorization, but may not be the best solution.