Exploring Sites.Selected Permission in Azure AD Apps

Sites.Selected is an application permission granted to Azure AD apps, addressing a significant limitation that previously existed. Granting permissions such as Sites.Read.All and Sites.ReadWrite.All in an Azure AD app would apply those permissions to all sites in the tenant, which contradicts the security principle of least privileged access. Informed developers understood the implications and refrained from requesting such permissions, and even tenant administrators would reject such requests. This functionality was available in legacy SharePoint provider-hosted apps, where developers could register their apps on a specific site’s appregnew.aspx and grant application permissions limited to that particular site through appinv.aspx. However, considering the recommendation to use Azure AD Apps, this limitation needed to be addressed by Microsoft and they released the functionality. Official documentation here – https://devblogs.microsoft.com/microsoft365dev/controlling-app-access-on-specific-sharepoint-site-collections/ and recent blog from support team https://techcommunity.microsoft.com/t5/microsoft-sharepoint-blog/develop-applications-that-use-sites-selected-permissions-for-spo/ba-p/3790476

I will share my experience working with it in this blog post.

First step is to add the permission “Sites.Selected” under Microsoft Graph to an Azure AD app that will use Microsoft Graph API to access SharePoint site. We have “Sites.Selected” under SharePoint also, this is used if SharePoint REST API/CSOM used to access SharePoint sites. This post doesn’t cover that, that’s for later.

The next step involves granting the Azure AD app or service principal the required access to specific site(s). This can be achieved using different methods. Here are a few options that have been tried and tested:

  1. Graph Explorer: If the Graph Explorer app is already available or the administrator is willing to add it to the tenant, this provides a straightforward approach. By utilizing Graph Explorer, you can make API calls to assign the necessary permissions to the Azure AD app/service principal on the desired site(s).
  2. Graph PowerShell SDK: This method leverages the “Microsoft Graph Command Line Tools” enterprise app rather than Graph Explorer. Using the Graph PowerShell SDK, you can automate the process of granting access to specific site(s) for the Azure AD app/service principal.
  3. PnP PowerShell SDK: In this approach, the “PnP Management Shell” is utilized instead of the aforementioned enterprise apps. The PnP PowerShell SDK enables the management of SharePoint operations, including granting access to selected site(s) for the Azure AD app/service principal.

In all of these methods, the fundamental requirement is to have an Azure AD app with the “Sites.FullControl.All” permission, granted either as delegated or application permission by the administrator. Additionally, you can create a custom Azure AD app registration with the necessary permission to accomplish the same goal.

4. Create new Azure AD App registration with the same permission “mentioned above”Sites.FullControl.All”.

So far, we have ensured that we have the necessary app registration and permissions in place to grant access to the first Azure AD app with the “Sites.Selected” permission to a specific SharePoint site. By ensuring the availability of the required app registration and obtaining the necessary permission, we have set the stage for granting the desired access to the selected SharePoint site(s) for the first Azure AD app. Now we will see how its done.

Graph Explorer or custom Azure AD app

The Graph API endpoint ‘Create permission’ is used to grant an Azure AD application permission to access a SharePoint site. https://learn.microsoft.com/en-us/graph/api/site-post-permissions?view=graph-rest-1.0&tabs=http

Allowed Permissions – [read|write|manage|fullcontrol]

POST https://graph.microsoft.com/v1.0/sites/{sitesId}/permissions
Content-Type: application/json

{
  "roles": ["write"],
  "grantedToIdentities": [{
    "application": {
      "id": "[Azure AD App Client Id]",
      "displayName": "Test Sites.Selected"
    }
  }]
}

Graph PowerShell SDK

$ErrorActionPreference = 'Stop'
# INPUTS
$appId = "[Azure AD App Client Id]"
$appDisplayName = "Test Sites.Selected"
$siteId = "[Site GUID]"
$permission = "read"

# CONNECT
Connect-MgGraph -Scopes "Sites.Read.All","Sites.FullControl.All"

# TEST - Get Current Permissions 
Write-Host "Before the update" -ForegroundColor Green
$permissions = Get-MgSitePermission -SiteId $siteId
$permissions | Select-Object -ExpandProperty GrantedToIdentities | Select-Object -ExpandProperty Application

# ADD - new Service Principal
$body = @{
  'roles' = @(
      $permission
   );
  'grantedToIdentities' = @(
      @{
        'application' = @{
          'id' = $appId
          'displayName' = $appDisplayName
        }
      }
   );
}
$bodyJson = $body | ConvertTo-Json -Depth 10 -Compress
Invoke-MgGraphRequest `
    -Method POST `
    -Uri "v1.0/sites/$siteId/permissions" `
    -Body $bodyJson

# TEST - Get Updated Permissions 
Write-Host "After the update" -ForegroundColor Green
$permissions = Get-MgSitePermission -SiteId $siteId
$permissions | Select-Object -ExpandProperty GrantedToIdentities | Select-Object -ExpandProperty Application

PnP PowerShell SDK

Connect-PnPOnline -Url https://[tenant].sharepoint.com/sites/[sitename] -Interactive

Grant-PnPAzureADAppSitePermission -AppId '[Azure AD App Client ID]' -DisplayName '[Azure AD App Display Name]' -Site 'https://[tenant].sharepoint.com/sites/[sitename]' -Permissions Read

You can now proceed to test various operations on the SharePoint site, such as reading or writing SharePoint list items, depending on the specific permission granted to the Azure AD application.

Unlocking Hidden Power: Granting Permissions via appinv.aspx – An Alternative Approach

As a bonus, there is an interesting option available for granting permissions to an Azure AD application on a SharePoint site. This option is not documented by Microsoft, so it’s important to exercise caution and make decisions based on your own discretion.

The appinv.aspx page is a SharePoint legacy feature that allows you to grant permissions to SharePoint provider-hosted apps. Although it is not officially documented by Microsoft, some developers have explored its usage for granting permissions to Azure AD applications as well.

To utilize the appinv.aspx page, you can follow these steps:

  1. Navigate to the SharePoint site where you want to grant permissions to the Azure AD application.
  2. Append “/_layouts/15/appinv.aspx” to the site URL, and access the appinv.aspx page.
  3. On the appinv.aspx page, you can specify the App Id of the Azure AD application that you want to grant permissions to.
  4. In the “Permission Request XML” field, you can define the specific permissions and their levels that you want to grant to the Azure AD application. The format for defining permissions follows the SharePoint permission schema. Sample xml below.
  5. Click the “Create” button to save the changes and grant the permissions to the Azure AD application.
<AppPermissionRequests AllowAppOnlyPolicy="true">
  <AppPermissionRequest Scope="http://sharepoint/content/sitecollection" Right="FullControl" />
</AppPermissionRequests>


For permissions xml you can check this documentation. https://learn.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azureacs

Indeed, one interesting aspect of utilizing the appinv.aspx approach is that it allows the site collection administrator to grant permissions directly on the SharePoint site. Unlike the other approaches that involve using an Azure AD app with “Sites.FullControl.All” permission, which requires admin consent, this method provides an alternative option that does not rely on additional Azure AD apps or admin involvement.

By leveraging the appinv.aspx page, the site collection administrator can have more control over granting permissions to the Azure AD application without the need for external dependencies. This can be particularly useful in scenarios where administrative overhead or obtaining admin consent for additional Azure AD apps is not feasible or desirable.

However, it’s important to note that this approach is not officially documented by Microsoft and may have its own limitations and considerations. It’s recommended to thoroughly understand the implications and perform thorough testing before implementing it in a production environment.

On-Behalf-Of flow for Downstream API Calls in Teams Apps with SSO: 4 Ways to Get it Done

Option 1: Use the OAuth Protocol Directly

The OAuth 2.0 protocol provides a way for a user to grant a third-party application access to their resources without sharing their credentials. To implement the on-behalf-of flow using the OAuth protocol, you can use the OAuth 2.0 assertion grant type with a JWT token that represents the signed-in user.

To use the OAuth protocol directly, you will need to build a request to exchange the user access token for an access token to access the downstream API on behalf of the user. You can then send the request to the token endpoint and get a response that contains the access token.

Here is the code snippet to build the request and exchange the token:

private async Task<string> ExchangeUserAccessTokenForOnBehalfOfAccessToken(
    string userAccessToken,
    string clientId,
    string clientSecret,
    string tokenEndpoint,
    IEnumerable<string> scopes)
{
    var requestBody = new List<KeyValuePair<string, string>>
    {
        new KeyValuePair<string, string>("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"),
        new KeyValuePair<string, string>("assertion", userAccessToken),
        new KeyValuePair<string, string>("client_id", clientId),
        new KeyValuePair<string, string>("client_secret", clientSecret),
        new KeyValuePair<string, string>("scope", string.Join(" ", scopes)),
        new KeyValuePair<string, string>("requested_token_use", "on_behalf_of"),
    };
    var requestContent = new FormUrlEncodedContent(requestBody);

    using (var client = new HttpClient())
    {
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        var response = await client.PostAsync(tokenEndpoint, requestContent);

        if (response.IsSuccessStatusCode)
        {
            var responseContent = await response.Content.ReadAsStringAsync();
            var tokenResponse = JsonConvert.DeserializeObject<TokenResponse>(responseContent);
            return tokenResponse.AccessToken;
        }
        else
        {
            var responseContent = await response.Content.ReadAsStringAsync();
            throw new Exception($"Failed to exchange token: {responseContent}");
        }
    }
}

Option 2 – Use Microsoft.Identity.Client and ConfidentialClientApplicationBuilder

Step 1: Install Required Packages

The first step is to install the necessary packages. In this case, we will need the Microsoft.Identity.Client package.

Install-Package Microsoft.Identity.Client

Step 2: Acquire Token using On-Behalf-Of Flow

In this step, we will use the ConfidentialClientApplicationBuilder class to acquire an access token for the downstream API using the On-Behalf-Of flow. The following code snippet shows how to do this:

// Create a Confidential Client Application builder and configure it with the app's Client ID and Client Secret
var app = ConfidentialClientApplicationBuilder.Create(clientId)
    .WithClientSecret(clientSecret)
    .WithAuthority(new Uri(authority))
    .Build();

// Acquire the On-Behalf-Of access token
var result = await app.AcquireTokenOnBehalfOf(scopes, new UserAssertion(userAccessToken))
    .ExecuteAsync();

// Get the access token from the result
var accessToken = result.AccessToken;

Here, clientId is the Client ID of the app, clientSecret is the Client Secret of the app, authority is the authority URL (e.g., https://login.microsoftonline.com/yourtenant.onmicrosoft.com), scopes is an array of scopes required to access the downstream API, and userAccessToken is the user’s access token obtained from the Teams app.

Option 3 – Use Microsoft.Identity.Web and AddTokenAcquisition middleware

To use Microsoft.Identity.Web in your application, you need to add the Microsoft.Identity.Web NuGet package to your project.

In this option, we will be using the AddTokenAcquisition middleware to acquire the access token for the downstream API. The AddTokenAcquisition middleware uses the token acquisition service provided by Microsoft.Identity.Web to acquire tokens for downstream APIs.

Here’s the code snippet for configuring the services in your application startup:

using Microsoft.Identity.Web;

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"));

    services.AddAuthorization();

    // Add token acquisition service
    services.AddTokenAcquisition();
}

With the AddTokenAcquisition middleware, we can now use the GetTokenAsync method to acquire the access token for the downstream API. Here’s an example of how to use the GetTokenAsync method in a controller action

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Identity.Web.Resource;

[Authorize]
[ApiController]
[Route("[controller]")]
public class SampleController : ControllerBase
{
    private readonly ILogger<SampleController> _logger;

    public SampleController(ILogger<SampleController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public async Task<IActionResult> Get([FromServices] ITokenAcquisition tokenAcquisition)
    {
        string[] scopes = new string[] { "https://graph.microsoft.com/.default" };

        string accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(scopes);

        _logger.LogInformation($"Access token: {accessToken}");

        // Use the access token to call the downstream API

        return Ok();
    }
}

In the code above, we are using the ITokenAcquisition interface to acquire the access token for the downstream API. We are also using the [Authorize] attribute to require authentication for the action.

We are then calling the GetAccessTokenForUserAsync method to acquire the access token for the Graph API. We are passing the Graph API scope as a parameter to the method.

Once we have acquired the access token, we can use it to call the downstream API.

Option 3 is a good option if you’re looking for a simple and easy-to-use way to acquire access tokens for downstream APIs.

Option 4: Enable On-Behalf-Of Flow with Microsoft Graph using AddMicrosoftGraph Middleware

Option 4 involves using the Microsoft Graph API to implement on-behalf-of flow. This is the simplest option as it involves using the AddMicrosoftGraph middleware and GraphServiceClient to make Graph API calls.

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(Configuration)
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddMicrosoftGraph(Configuration.GetSection("GraphApi"));

In the startup class, we add authentication using JwtBearerDefaults.AuthenticationScheme and the Microsoft Identity Web API with the AddMicrosoftIdentityWebApi method. We then enable token acquisition to call downstream APIs using the EnableTokenAcquisitionToCallDownstreamApi method and add the Microsoft Graph API with the AddMicrosoftGraph method. Finally, we add distributed token caches with the AddDistributedTokenCaches method.

This option is simple and efficient as it does not require additional configuration or code to implement on-behalf-of flow. It utilizes the power of the Microsoft Graph API and the middleware provided by Microsoft.Identity.Web to streamline the authentication process.

using Microsoft.Graph;
using Microsoft.Identity.Web;
using System.Collections.Generic;
using System.Threading.Tasks;

[Authorize]
[ApiController]
[Route("[controller]")]
public class SharePointController : ControllerBase
{
    private readonly GraphServiceClient _graphServiceClient;

    public SharePointController(GraphServiceClient graphServiceClient)
    {
        _graphServiceClient = graphServiceClient;
    }

    [HttpGet("listItems")]
    public async Task<IEnumerable<SharePointListItem>> GetListItems()
    {
        var listItems = await _graphServiceClient.Sites["<site-id>"].Lists["<list-id>"].Items
            .Request()
            .Select(x => new { x.Id, x["Title"] })
            .GetAsync();

        var items = new List<SharePointListItem>();
        foreach (var listItem in listItems)
        {
            items.Add(new SharePointListItem
            {
                Id = listItem.Id,
                Title = listItem["Title"].ToString()
            });
        }
        return items;
    }
}

ClientContext Technical Challenge – Some Item Fields not updated

Technical Challenge:

If  a ClientContext is used to track a list item and some changes are made to the tracked list item (Say, a few fields are assigned values), same context is used to execute some operation (executeQuery is called) but the listItem.Update() is not called. After this, rest of the fields are assigned and item.Update() is called along with executeQuery.

Developer generally expects that the item will be updated with all the fields as set. However, the first set of fields will not have the values as set. What happened here is the executeQuery call which was made after the first set of fields had been set, sends the field assignments in the request payload but Item.Update() method is not present, so SharePoint doesn’t update the item. Once the executeQuery returns, it doesn’t track the first set of field as those were already sent in the payload , so the final executeQuery will only have the last set of field assignments in the request payload.

 ClientContext executeQuery works this way either because of deliberate decision in the technical design or a technical bug

Approaches to avoid the issue:

  • In order to keep tracking the field assignments or any changes that are associated with a client context, if there’s a need for an additional executeQuery before the tracked changes are sent to SharePoint, use a different context for that additional operation –
    • Different context can be a cloned context of the existing one
    • A new context altogether

Fiddler Setup on Windows

  • Install “Fiddler Everywhere”
  • Enable Capture HTTPS traffic if needed. (For SPO calls, it’s needed)
  • Trust root certificate. It prompts you to trust. It will be added to “Trusted Root Certificate Authorities” of the current user certificates
  • Export the certificate and import it to “Trusted Root Certificate Authorities” of the local machine so that the certificate is trusted for every user of the machine.
  • The above step is needed to capture traffic from processes that are running under different user such as Application Pool Identity in case of IIS Sites

Set the port to 8888 (If this port is already used by some other application, use a different port)

Enable

That’s all it requires to setup Fiddler to capture HTTP(S) traffic on Windows Server/Client OS.

You can see the traffic in Fiddler

If Fiddler is not capturing traffic from IIS Sites, then follow the below steps.

Add below spippet at the correct place to web.config of the IIS site you want to capture the outbound traffic

<system.net>
    <defaultProxy>
      <proxy
        usesystemdefault=”False”
        bypassonlocal=”True”
        proxyaddress=”http://127.0.0.1:8888″/&gt;
    </defaultProxy>
  </system.net>

Modifying Content Type Field Properties

I have a custom page layout attached to a content type that has a field “Service”. In the page layout, I used SharePoint Server-side control to render the field in the edit form. The label of the control is the display name of the field. I have got a request to rename the field from Service to Business in the application. I made the change to the site column from UI but it failed. I suspect it’s because of the number of subsites present. There are about more than 2000 subsites. So I did the change from PowerShell using object model. I thought the site content types and list content types referencing it would also be updated, but it didn’t do that. It only updated the list column. This drives a point that site column and list column referencing it are related but content type won’t maintain a relationship with site column. That seems to be true from my analysis. So to update the display name of the filed in the content type, I need to update the field display name in the content type and propagate the changes to the list content types. The following script helped me do it.

$Web= Get-SPWeb "root web url"
$pageCT = $web.ContentTypes["CType name"]
$field = $pageCT.FieldLinks["Column"]
$field.Title = "New Display Name"
$pageCT.Update($true)

References:
http://paulryan.com.au/2013/ct-field-props/
http://blog.furuknap.net/list-columns-in-a-sharepoint-content-type-how-is-that-possible
Update Content types
Update child content types
https://soerennielsen.wordpress.com/2007/09/11/propagate-site-content-types-to-list-content-types/
https://soerennielsen.wordpress.com/2007/09/08/convert-%E2%80%9Dvirtual-content-types%E2%80%9D-to-physical/

CAML query to check if the people field has the user or any group that user belongs to

It’s a custom application and we show the list of items that the current user has permissions. Permissions are managed using user field instead of item level permissions. Now to retrieve the relevant data for the current user, we need to verify if the user or any group that user belongs to is part of the user field. Initially, we thought SharePoint CAML can’t do this job. However, It turns out that CAML is more powerful. We could achieve it with Membership element. For the CAML, please check out the gist.
This will work with SharePoint Groups and AD security groups. Not sure if it works with AD Distribution group.

<Where>
<Or>
<Eq>
<FieldRef ID=’Assigned To’ />
<Value Type=’Integer’><UserID/></Value>
</Eq>
<Membership Type=’CurrentUserGroups’>
<FieldRef Name=’AssignedTo’ />
</Membership>”;
</Or>
</Where>

Static Constructor and Exceptions

No matter what you are doing in the static constructor, make sure it never throws an exception. In case the code might throw an exception, and the exception should not be handled as it is a fatal exception and the program should not continue, It’s okay to have that code in the static constructor.
But if the exception can be handled out side the static constructor once it’s thrown from the static constructor,don’t put such code in the static constructor for a simple and obvious reason that static constructor is called only once in the app domain. After the exception is handled, if you expect the static constructor will work again to set it’s static variables correctly, it won’t do that. The exception that you see when Static constructor fails is TypeInitializationException.

Calling external service or external systems with windows authentication from SharePoint Webparts/Pages

Let’s take the assumption that the external service or external system (SQL Server) in the problem accepts windows authentication and the SharePoint web application is configured to use claims mode authentication.  The following post I describe is applicable when you have the assumptions met.

You get 401 unauthorized when you call any external service/system such as WCF, Web API, SQL server etc. from the web part/page. We know that the code inside web part is run with logged in user permissions.  Say, for example, I have code in web part that writes to a SharePoint list. If the logged in user doesn’t have permission, the code fails. This example is mentioned only to emphasize the well-known point that the code runs with logged in user permissions.

When a call is made to external service, I’m not sure which user account is used to authenticate to the service. I couldn’t wrap my head around it in the short time I’ve analyzed the issue. I am curious to know the underpinnings of this authentication and IIS impersonation, so I hope to publish another post on that.

Let’s come to the point why we get 401 unauthorized. SharePoint web part code has claims token of the logged in user or the app pool account. In claims mode, SharePoint only has claims token of the user. When it requests external service, it sends the claims token but the external service as we said in the start accepts only windows token and it can’t understand the authentication information it receives and fails to authenticate and thus we see response status code 401.

The root cause of this problem hints us about the resolution too. We have claims token of the logged in user, convert it to windows token and send that token to the service. That’s it about the resolution. We must have a successful request now.  In theory, this is it. Basically, in claims mode, the WindowsIdentity of the user does not exist because it is created as  IClaimsIdentity (that is, in .NET 3.5, an interface that inherits IIdentity). For this reason, developer must call a special .NET WCF service called C2WTS (claims to windows token service) that will return a WindowsIdentity that can be used for delegation.

Now let’s see the meat (i.e. the code)

   
public static WindowsIdentity GetWindowsIdentityForCurrentClaimUser()
{
    IClaimsIdentity identity = Thread.CurrentPrincipal.Identity as ClaimsIdentity;
    string upn = null; 
    if (identity != null)
    {
          foreach (var claim in identity.Claims)
          {                  if(StringComparer.Ordinal.Equals(System.IdentityModel.Claims.ClaimTypes.Upn, claim.ClaimType))
                    {
                        upn = claim.Value;
                    }
                }
            } 
            WindowsIdentity windowsIdentity = null;

            if (!String.IsNullOrEmpty(upn))
            {
                try
                {
                    SPSecurity.RunWithElevatedPrivileges(delegate
                    { 
                        windowsIdentity = S4UClient.UpnLogon(upn);
                    });
                }
                catch (SecurityAccessDeniedException e)
                {
                    throw;
                }
            } 
            return windowsIdentity;
        }

The above method returns WindowsIdentity (windows token). The following is the way to call web service under windows impersonation context.

using (WindowsImpersonationContext ctxt = Utility.GetWindowsIdentityForCurrentClaimUser.Impersonate())
{
	//Call the web service here...
}

OWAS 2013 with SharePoint server 2013

This post, I would like to discuss about how to install OWAS 2013 (Office Web Apps Server) and configure SharePoint Server 2013 to use it.

What do we get with it?
OWAS 2013 is responsible for the document preview and edit in SharePoint server 2013. Besides SharePoint server, OWAS 2013 can be leveraged by Lync 2013 and Exchange 2013.

About OWAS 2013
OWAS 2013 can’t be installed as a service on SharePoint server. It should be installed as a standalone server. No other server products like SQL, SharePoint, Lync, Exchange, and Office etc. should be installed on the same server that OWAS 2013 is installed on. OWAS 2013 can be installed only on Windows server 2012 and 2008 R2. This can’t be installed on Server 2008 (You need R2).

The following is a snapshot taken from Technet (http://technet.microsoft.com/en-us/library/jj219455(v=office.15).aspx) which depicts the steps involved in setting up OWAS and any host that uses it.

Steps to deploy OWAS

Steps to be done on OWAS server
1) OWAS Download and Install
2) Setup OWAS

Steps to be done on SharePoint server
1) Configure with OWAS

OWAS Download and Install
You can download it from the link below and follow the instructions mentioned there. Installation is pretty simple.
http://www.microsoft.com/en-us/download/details.aspx?id=35489

Setup OWAS
The following PowerShell cmdlets help you create Office Web apps farm once OWAS server is installed.
Use either of the following two cmdlets. Use Https cmdlet if you want that support.

HTTP Only
New-OfficeWebAppsFarm –InternalURL "http://spworks01.corp.spworks.net" –AllowHttp –EditingEnabled

http://spworks01.corp.spworks.net - FQDN of server

HTTPS
New-OfficeWebAppsFarm -InternalUrl "https://spworks01.corp.spworks.net" -ExternalUrl "https://spworksowas.spworks.com" -CertificateName "SpWorksOWAS" –EditingEnabled

If Windows PowerShell doesn’t recognize “New-OfficeWebAppsFarm” cmdlet, you need to import the corresponding module by running the following command.
Import-Module –Name “OfficeWebApps”

Before you proceed to SharePoint server verify that OWAS is working with the following url.
http://servername/hosting/discovery (server name is the “internal url” given when setting up Office Web Apps server). If OWAS is properly configured, you’ll see the following xml.
Wopi-Discovery-XML

SharePoint Server configuration with OWAS
Run the SharePoint Management shell as administrator.
Run the following commands.
The following command helps set the WOPI binding with OWAS
New-SPWOPIBinding -ServerName "spworksowas.spworks.com"

Set the SPWOPIZone to internal-http

Get-SPWOPIZone
external-https

Set-SPWOPIZone -zone "internal-http"
Get-SPWOPIZone
internal-http

AllowOAuthOverHttp

(Get-SPSecurityTokenServiceConfig).AllowOAuthOverHttp
False
$config = (Get-SPSecurityTokenServiceConfig)
$config.AllowOAuthOverHttp = $true
$config.Update()
(Get-SPSecurityTokenServiceConfig).AllowOAuthOverHttp
True

Now SharePoint document preview and editing should be working.

References
Plan Office Web Apps Server
http://technet.microsoft.com/en-us/library/jj219435(v=office.15)

Configure OWAS for SharePoint 2013
http://technet.microsoft.com/en-us/library/ff431687(v=office.15).aspx
http://blogs.technet.com/b/speschka/archive/2012/07/23/configuring-office-web-apps-in-sharepoint-2013.aspx

Deploy OWAS
http://technet.microsoft.com/en-us/library/jj219455(v=office.15).aspx