After an application call to a
SharePoint API has been authenticated, the next step in the chain of
security processing is to check whether the app and user have the
appropriate rights to the resources they are attempting to access.
These permissions can be assigned two ways:
Statically assigned permissions are defined as one or more permission requests in the AppManifest.xml
file. They are defined by the application developer and are the
permissions that the app requests when it is installed. When a user
adds the app to the site, she is presented with a consent dialog
screen, as shown in Figure 1, asking for the user to grant the permissions being asked for.
Note that granting permissions is an “all or
nothing” operation. A user cannot, for example, only grant one of the
two permissions being asked for. This is another example of why asking
only for the permissions that app needs at a minimum to run is
important.
After a user grants the application the
appropriate permissions, they are recorded in SharePoint in the
application management shared service. They are then referred to when
an app makes an API call for access to resources.
Additionally, an application may dynamically
request permissions on the fly during execution. This allows for
scenarios where the application might not know what resources it needs
to begin with, or when it might start with only basic rights and then
request more as appropriate. This is discussed more in the next
section, “Requesting Permissions Dynamically.”
NOTE
When designing your application, only statically define the minimum
permissions the app needs to run. Defining permissions that give the
application full control over an entire site will cause the user and
admin concern if these permissions are not needed.
Permission requests for apps are analogous to
granting permissions for a user to access a resource. For example,
granting a user contributor access on a document library is the same as
granting an application read/write-access on that library.
You statically define permission requests in the AppManifest.xml file. Here is an example:
<AppPermissionRequests>
<AppPermissionRequest Scope="http://sharepoint/content/sitecollection/web"
Right="FullControl" />
</AppPermissionRequests>
Two pieces of information are needed in a permissions request:
The scope defines what component the permission request is for. It is not
a URL although it looks like one. These components (also referred to as
a permissions provider) are from a defined list. They cover various
aspects of SharePoint such as sites, lists, and libraries. Some example
scopes include the following:
- http://sharepoint/search
- http://sharepoint/content/sitecollection/web
- http://sharepoint/content/sitecollection/web/list
- http://sharepoint/projectserver
- http://sharepoint/social/microfeed
- http://sharepoint/taxonomy
NOTE The preceding list is not exhaustive and only shows a short list of the possible scopes.
The right
defines what level of access is being requested. For example, the
application might be asking only for read-access to a particular
resource versus read/write access. The various rights available vary
for each particular scope. The most common rights are:
- Read
- Write
- Manage
- FullControl
By combining a scope with a right, developers can define a vast number of permissions.
Requesting Permissions Dynamically
Many situations exist where an
application might not know ahead of time what permissions it needs.
Take, for example, the situation where an app reads documents in
document libraries and does something with them. In this situation the
app will not know ahead of time what document libraries those documents
reside in, or how many document libraries they are stored in. This
means the application might need to request permissions dynamically
from the user. SharePoint 2013 has a method for doing this by prompting
the user to allow additional permission requests by redirecting the
user to another consent dialog. The TokenHelper class has a helper method to assist with constructing the URL for the consent page:
TokenHelper.GetAuthorizationUrl(sharePointSiteUrl.ToString(),
"Web.Read List.Write",
"https://myapp.com/RedirectAccept.aspx"));
This helper method returns a URL pointing at the OAuthAuthorize.aspx
page along with the permissions you are requesting, the app client ID,
scope, and the redirect-accept URI. You can then direct the user to
that URL using Response.Redirect().
Another possibility is to pop up a modal dialog with JavaScript
containing an iFrame to this page. After the permissions request is
complete, SharePoint redirects back to the URI you specify (in the
preceding example, RedirectAccept.aspx) along with an authorization code.
https://myapp.com/RedirectAccept.aspx?code=authcode
That authorization code found in the query string parameter code can then be used to request an access token that includes those additionally granted permissions. The TokenHelper class has a method for assisting with this:
TokenHelper.GetAccessToken(authorizationCode, targetPrincipalName,
targetUri.Authority, targetRealm, redirectUri).AccessToken;
This access token is also cacheable. It will
expire, however, so you should also cache the refresh token given back,
which will allow the app to request additional access tokens when they
expire.
TokenHelper also has a method for getting a CSOM ClientContext object directly using the authorization code:
TokenHelper.GetClientContextWithAuthorizationCode(targetUrl,targetPrincipalName,
authorizationCode, targetRealm, redirectUri)
App-Only Policy
The three “contexts” in which authorization is validated in SharePoint are as follows:
- User-only policy
- App+user policy
- App-only policy
User-only policy
is what is evaluated when users are using the SharePoint Web UI.
SharePoint checks their permissions to do certain actions/activities
and either allows or denies the activity.
The app+user policy
is typically used when an app makes an API call to SharePoint. In this
policy both the app and the user must have appropriate
permissions/rights in SharePoint to complete the action successfully.
If the call is being made to a document library the user does not have
access to, the call will fail — access is denied. Likewise, if the app
does not have permissions, the call will also fail.
In some situations an application might not want
or need to act on behalf of a user. Additionally, there may be
instances when you want to temporarily allow users to take actions on
resources that they don’t have explicit permissions on. This later
scenario is analogous to temporarily elevating permissions using the
full-trust code model of the past.
In these scenarios the application model allows
apps to make calls to SharePoint only in the context of the application
and not the user. This is called “app-only” context. Therefore, app-only policy
means that only the app’s permissions are checked when the call is
made, so if the user does not have access to a resource but the app
does, the call will still succeed. As mentioned earlier, this is a good
way to temporarily elevate permissions for a user. The app can be
granted permissions on things that the user doesn’t normally have
access to. An example of when this could be useful is for situations
such as submitting a document to a secure location or perhaps approving
a document that the user doesn’t have access to via SharePoint and only
has access to via the application.
To use the app-only policy you must do two things:
- Allow app-only permission in the app manifest.
- Create an app-only access token.
An app defines that it needs app-only access in the AppManifest.xml file by the developer setting the AllowAppOnlyPolicy attribute as follows:
<AppPermissionRequests AllowAppOnlyPolicy="true">
NOTE
Because of the additional capabilities this access allows, only certain
site collection administrators can grant applications requesting this
permission.
At run time an app-only access token must be created in order to make calls just like the application does. Again, the TokenHelper class makes this easy with the following helper function:
TokenHelper.GetAppOnlyAccessToken(contextToken.TargetPrincipalName,
sharepointUrl.Authority, contextToken.Realm).AccessToken;