Office 365 iOS SDK - How to invoke SharePoint REST API

2.5k Views Asked by At

All the iOS SDK samples provide working code for accessing Mail, Calendar, ODfB FIles, but none show how to access SharePoint list items. So I am trying a simple REST call in Swift, but keep getting the following error:

[0] (null) @"error_description" : @"Unsupported security token.

Here is a subset of my code when my App starts:

var resourceID : String = "https://mytenant.sharepoint.com"
var authorityURL : String = "https://login.windows.net/common/"
var clientID : String = "xxd4200eb-7284-41be-a434-abb269b82f0f"
var redirectURI : NSURL = NSURL(string: "http://www.mycompanywebsite.com")!

override func viewDidLoad() {
    super.viewDidLoad()

    var defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()

    var er : ADAuthenticationError? = nil
    var authContext:ADAuthenticationContext = ADAuthenticationContext(authority: authorityURL, error: &er)

    authContext.acquireTokenWithResource(resourceID, clientId: clientID, redirectUri: redirectURI) { (result: ADAuthenticationResult!) -> Void in
        if (result.accessToken == nil) {
            println("token nil")
        } else {
            defaults.setObject(result.accessToken, forKey: "accessTokenDefault")
            defaults.synchronize()
            println("accessToken: \(result.accessToken)")
        }
    }
}

Then, once I get the Token, I invoke the following code that tries an http GET but fails:

    var resolver : MSODataDefaultDependencyResolver = MSODataDefaultDependencyResolver()

    var credentials : MSODataOAuthCredentials = MSODataOAuthCredentials()
    var defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()

    credentials.addToken(defaults.objectForKey("accessTokenDefault") as! String)
    var credentialsImpl : MSODataCredentialsImpl = MSODataCredentialsImpl()
    credentialsImpl.setCredentials(credentials)
    resolver.setCredentialsFactory(credentialsImpl)

    //build API string to get a sample list info
    let request = NSMutableURLRequest(URL: NSURL(string: "https://umaknow.sharepoint.com/_api/web?$select=Title")!)
    request.HTTPMethod = "GET"
    let token = defaults.stringForKey("accessTokenDefault")
    request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
    request.setValue("application/json; odata=verbose", forHTTPHeaderField: "accept")

    //make the call to the SharePoint REST API
    NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse!, data: NSData!, error: NSError!) -> Void in
        var error:NSError? = nil
        let jsonResult: NSDictionary! = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: &error) as? NSDictionary

        if (jsonResult != nil) {
            //parse the json into File objects in the table view
            let results:NSArray = (jsonResult["d"] as! NSDictionary)["results"] as! NSArray

And this is where it fails with the error message. Monitoring the web traffic, the following is a bit more details about what is going on:

This is my request (RAW):

GET /_api/web?$select=Title HTTP/1.1
Host: mytenant.sharepoint.com
Connection: keep-alive
Proxy-Connection: keep-alive
Accept: application/json; odata=verbose
User-Agent: O365Demo/1 CFNetwork/711.3.18 Darwin/14.3.0
Accept-Language: en-us
Authorization: Bearer Optional("eyJ0eXAiOiJKV1QiLDJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSIsImtpZCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSJ9.eyJhdWQiOiJodHRwczovL3VtYWtub3cuc2hhcmVwb2ludC5jb20iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC81NmZjOTc3OC04YWFjLTQ1ZDItOTMwNS1iOTE3MWZmYWZhOGMvIiwiaWF0IjoxNDM1MjI3Nzk1LCJuYmYiOjE0MzUyMjc3OTUsImV4cCI6MTQzNTIzMTY5NSwidmVyIjoiMS4wIiwidGlkIjoiNTZmYzk3NzgtOGFhYy00NWQyLTkzMDUtYjkxNzFmZmFmYThjIiwib2lkIjoiOGUyZTBlZDQtYmEzNC00YWM4LTkwYmMtNWQ3NGQ3MzE4YjkyIiwidXBuIjoicGllcnJlQHVtYWtub3cuY29tIiwicHVpZCI6IjEwMDMwMDAwODUyMUY3NjYiLCJzdWIiOiJqR3BHQ1VDdDNYTnM2b0pjSkgxVldQOUwyV1JLa3lIOHhxWHlKbVFaSV8wIiwiZ2l2ZW5fbmFtZSI6IlBpZXJyZSIsImZhbWlseV9uYW1lIjoiQm91cmFzc2EiLCJuYW1lIjoiUGllcnJlIEJvdXJhc3NhIiwiYW1yIjpbInB3ZCJdLCJ1bmlxdWVfbmFtZSI6InBpZXJyZUB1bWFrbm93LmNvbSIsImFwcGlkIjoiNmQ0MjAwZWItNzI4NC00MWJlLWE0MzQtYWJiMjY5YjgyZjBmIiwiYXBwaWRhY3IiOiIwIiwic2NwIjoiQWxsU2l0ZXMuTWFuYWdlIEFsbFNpdGVzLlJlYWQgQ2FsZW5kYXJzLlJlYWRXcml0ZSBGaWxlcy5SZWFkIEdyb3VwLlJlYWQuQWxsIEdyb3VwLlJlYWRXcml0ZS5BbGwgTXlGaWxlcy5SZWFkIE15RmlsZXMuV3JpdGUgU2l0ZXMuUmVhZC5BbGwgU2l0ZXMuU2VhcmNoLkFsbCBUZXJtU3RvcmUuUmVhZC5BbGwgVXNlci5SZWFkIFVzZXIuUmVhZFdyaXRlIFVzZXIuUmVhZFdyaXRlLkFsbCIsImFjciI6IjEifQ.aAkaEIFuOeiI0ZRydzaOBTl5wyqLDYBHfvbSj6nZAk4jQKBZF6BhJsAAnhu9qj8oMR2gUdVr3vCNgzefvlZxcf3u0k6R8g4176M-bU3rAABri9DjyaZJ24jMs1u-kL0h5Ee8mvNXSI7BF7Qv9JoeHIiXLei_SXba1s8mhdwMaw9Se9tl8MbBFPLDDBLXUa4YgC_rYWO7G7rw3JEe3GmEV9NffZ7zklXxd55P8fxtbz0-KhI0wbRHIXN69wAuC0jiqhJ4FCCGzLvTuuUbirhURrhi4UizYpLWqqnr0I8zWAMvr8WUXCWtZhPkzOZ5teqbvBwp1UwYui42O6S0PfYKzQ")
Accept-Encoding: gzip, deflate

And finally the RAW response from the site

HTTP/1.1 401 Unauthorized
Server: Microsoft-IIS/8.5
x-ms-diagnostics: 3000006;reason="Token contains invalid signature.";category="invalid_client"
SPRequestGuid: 84c9139d-807c-2000-0e59-4caf75bd097f
request-id: 84c9139d-807c-2000-0e59-4caf75bd097f
SPRequestDuration: 19
SPIisLatency: 1
X-Powered-By: ASP.NET
MicrosoftSharePointTeamServices: 16.0.0.4107
X-Content-Type-Options: nosniff
X-MS-InvokeApp: 1; RequireReadOnly
P3P: CP="ALL IND DSP COR ADM CONo CUR CUSo IVAo IVDo PSA PSD TAI TELo OUR SAMo CNT COM INT NAV ONL PHY PRE PUR UNI"
WWW-Authenticate: Bearer realm="56fc9778-8aac-45d2-9305-b9171ffafa8c",client_id="00000003-0000-0ff1-ce00-000000000000",trusted_issuers="00000001-0000-0000-c000-000000000000@*,https://sts.windows.net/*/,00000003-0000-0ff1-ce00-000000000000@90140122-8516-11e1-8eff-49304924019b",authorization_uri="https://login.windows.net/common/oauth2/authorize"
Date: Thu, 25 Jun 2015 11:12:40 GMT
Content-Length: 51

{"error_description":"Unsupported security token."}

So there is something obviously wrong with the way I use the Token provided, but with my very limited OAuth2 knowledge and the lack of samples, I am at a lost.

Any help is greatly appreciated!

1

There are 1 best solutions below

1
On

In case anyone gets the same issue, I finally found what was wrong. It has nothing to do with ADAL or SharePoint REST, but a syntactic error in Swift. The line that read:

request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")

generates the following in the HTTP request:

Authorization: Bearer Optional("eyJ0eXAiOi....

The "Optional("...") has something to do with the insertion of the token variable in the string. By just replacing the statement with:

request.setValue("Bearer " + token!, forHTTPHeaderField: "Authorization")

now generates the correct header in the HTTP request:

Authorization: Bearer eyJ0eXAiOi...

and I get the data I want back from the call.

So this is really a newbie Swift programmer error! :-)