Uploading files to OneDrive. 401 error

2019-08-18 06:53发布

问题:

I'm implementing uploading big files to OneDrive by using BITS protocol. Here is the documentation

I'm having the following issue. After a certain period of time of being logged in when I'm trying to create a session I'm receiving 401 error. Here's the complete response:

<NSHTTPURLResponse: 0x7d6ee0f0> { URL: https://cid-da1cedf5484811a9.users.storage.live.com/items/DA1CEDF5484811A9!373/IMG_0003.JPG } { status code: 401, headers {
"BITS-Packet-Type" = Ack;
"Content-Length" = 0;
Date = "Mon, 22 Dec 2014 09:50:22 GMT";
P3P = "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\"";
Server = "Microsoft-HTTPAPI/2.0";
"X-AsmVersion" = "UNKNOWN; 19.9.0.0";
"X-ClientErrorCode" = AccessDenied;
"X-MSNSERVER" = "SN3301____PAP101";
"X-QosStats" = "{\"ApiId\":0,\"ResultType\":2,\"SourcePropertyId\":0,\"TargetPropertyId\":42}";
"X-ThrowSite" = "4e1b.c093";
} }

And here's how my request looks:

{
Authorization = "EwCAAq1DBAAUGCCXc8wU/zFu9QnLdZXy+YnElFkAAYejThC8LqFmGJx4sT4Wv11wzA6Cj9tleMQ+PM4PGXSV/DAorkfRiVnt+KQnZRI90XL/FIfwTvhWmp6jFhsppM39GgwbQ7it0tSVCSp4cIFCFSphmV783o+MKPeuEsw79mRGL5Kz2lcrVUxQAq12vUZXnhkX4qiJj0RqMSdaFYghfVVmhkcGsJITOwIisFq/JyaRoffgcW7vZCu9/9Q1Jm62f+bcGur82sTo5ucwV4M7QNtl77nVjv3tPElcUDgZnTvCLuhIE6QHY6yHS+9blWVbaXJBBD0ZPjDbUdjIlbexto6VvXtjp+vELL8rMSJMYcGjN4AxbGmBDnjLm9Iy1RIDZgAACALsAIEvRqDnUAFmIs8toBlRzfxctJcm1wxfqY/531QSDMVG9pIISpNCPppHAa/blSV8+LbLO/hW5i/36/3RTLpFAiXkPxzbkI8OJSrVRDfKcXiK1x+kTNFtcxXLBlJSWYlMY9OcwT+v/JTQnoGD1z6L56zOX53Gt7SBZQ0of7e3QecxHLX7w9lqs0jBmRvwL9I/n0+r9r6CI3tTEnFODFyED9ToBCRwwjLLD8P4qWRXWC1BUL+20v2QCQYRKzSDxZkYa3WrPLA4PIEFdFp58/limBzDrGW4MaWi1UgaI/QF8gN5n+JLNE4YcZL8KaXFZ76zNWNhJFsg4Lctsu95l7oD9MkezDvpA8bhkoNp39rEvPjBEurrFkdHviv73Z/C1W+IoA4ww3ZAYtIt3THtvPE4wq2fUEcb+MhOIG7abZtu4P07+5Zy2UO1rGlChE/pvmtV4XXrrg3TLG5zAQ==";
"BITS-Packet-Type" = "Create-Session";
"BITS-Supported-Protocols" = "{7df0354d-249b-430f-820d-3d2a9bef4931}";
"X-Http-Method-Override" = "BITS_POST";
}

I can post code used to open session if you need, I don't post it now because it's pretty large.

OK, the first thing that comes to my mind is that access token has got outdated and I need to renew it somehow. BUT! Just before I perform request to retrieve user Id using the same access token:

- (void)retrieveUserId
{
NSString                  *urlString            = [NSString stringWithFormat:@"https://apis.live.net/v5.0/me?access_token=%@", commonAccessToken];
NSURL                     *url                  = [NSURL URLWithString:urlString];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession              *session              = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSURLSessionDataTask      *task                 = [session dataTaskWithURL:url
                                                         completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){

                                                             NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;

                                                             if (httpResponse.statusCode == 200)
                                                             {
                                                                 NSLog(@"Response retrieving user id %@", httpResponse);

                                                                 NSError      *parsingError     = nil;
                                                                 NSDictionary *parsedDictionary = [NSJSONSerialization JSONObjectWithData:data
                                                                                                                                  options:kNilOptions
                                                                                                                                    error:&parsingError];
                                                                 if (!parsingError)
                                                                 {
                                                                     commonUserId = parsedDictionary[@"id"];
                                                                     [self createUploadRequest];
                                                                 }
                                                                 else
                                                                 {
                                                                     dispatch_async(dispatch_get_main_queue(), ^{

                                                                         [self.delegate bitsClient:self didFailWithError:parsingError];

                                                                     });
                                                                 }
                                                             }
                                                             else
                                                             {
                                                                 dispatch_async(dispatch_get_main_queue(), ^{

                                                                     [self.delegate bitsClient:self didFailWithError:error];

                                                                 });
                                                             }
                                                         }];
[task resume];
}

And it works. This access token is OK when I get the user Id. But you can see that here unlike when trying to open a session the access token is put as a part of url, not as one of header fields. I'm not sure if it matters or not. By now I can't explain such a behaviour. If I logout and login again then it's OK again, I don't receive the 401 error anymore, so probably I really should renew the token, but how can it be done? One more thing for you to know - when I get the access token from LiveConnectSession I check if this session is expired and it's not. Its expires property is later than [NSDate date].

Also one more thing for you to know - despite I'm not able to open session and my request is being rejected with 401 error if I try to upload files by using Live SDK's LiveOperation the files are uploaded without any error.

I want to resume: After a certain period (seems like it is 24 hours but I'm not sure) of being logged in as OneDrive user I become unable to upload files using BITS protocol because I receive 401 error. At the same time the session seems to be not expired and I can still upload files by using LiveOperation. Also I can easily get user id using the same access token which is used when opening an upload session. If I logout and login again then again everything works like charm and I'm able to create session and upload files.

Seems like this is all that I can tell you, but if you need any additional info please tell me. Any help is extremely appreciated.

回答1:

The response you provided in your question definitely indicates that the issue is around authentication - specifically OneDrive is not recognizing the provided authentication. Expiration is the most likely explanation for that and so refreshing is probably the answer. If you requested the "wl.offline_access" scope, along with the others you require, you should get a "refresh_token" back as part of the OAuth 2.0 flow and as a result allow the creation of new "access_tokens" when the previous ones expire. I believe the Live SDK itself handles the refreshing process, but I'm not entirely familiar with that side of things - for now make sure that scope is requested, and we'll learn together if the SDK handles the rest :)



回答2:

Andrey, i am php developer and here is what i am doing in php:

const API_URL = 'https://apis.live.net/v5.0/';
const INITAPIURL = 'https://login.live.com/oauth20_token.srf';
const URI_AUTHORIZE = 'https://login.live.com/oauth20_authorize.srf'; 

1) getting access token and saving refresh token (function below generate URL to redirect user for oAuth)

public function getAuthorizeUrl($cburl) {
        $cburl=_SKYDRIVE_REDIRECT_URI;
        return self::URI_AUTHORIZE.'?client_id='.urlencode(_SKYDRIVE_API_CLIENT_ID).'&scope='.urlencode('wl.offline_access wl.skydrive wl.skydrive_update wl.emails wl.contacts_skydrive wl.contacts_photos').'&response_type=code&redirect_uri='.urlencode($cburl);
    }

2) after every 3300 seconds (access token live for 3600 sec) i refresh access token with refresh token directly on server (without user confirmation in browser)

$r = $this->http(self::INITAPIURL, 'POST', array("grant_type" => "refresh_token", "client_id" => _SKYDRIVE_API_CLIENT_ID, "client_secret" => _SKYDRIVE_API_SECRET, "redirect_uri" => _SKYDRIVE_REDIRECT_URI, "refresh_token" => $this->refreshtoken));

3) i am sending access token as header with curl for all my requests to API (except token refresh above)

$headers[] = "Authorization: Bearer " . $this->token;

and above steps are working when i use BITS (i just implemented BITS protocol use on perl and everything is working)

P.S. Thank you that you posted URL of folder by ID they had wrong URL in documentation :)