How to login to a site using POST request? (Swift,iOS) - ios

I want to create an iOS Application that logs into a website and parses the data from several pages of that site, while maintaining the login session.This is what I have done so far. I send a GET request to retrieve the EVENTVALIDATON and VIEWSTATE parameters required for the POST request. (I looked at the POST by using 'Firebug'). When I run the following code, it gives back the same login page. But it should be giving me this page.
var parameter: Parameters = [:]
var viewstate: String = ""
var eventvalidation: String =
#IBAction func postRequest(_ sender: Any) {
Alamofire.request("https://ecampus.psgtech.ac.in/studzone/AttWfLoginPage.aspx").responseString { response in
print("\(response.result.isSuccess)")
if let html = response.result.value {
if let doc = Kanna.HTML(html: html, encoding: String.Encoding.utf8) {
// Search for nodes by CSS selector
for show in doc.css("input[id='__VIEWSTATE']") {
self.viewstate=show["value"]!
//print(show["value"] as Any)
}
for show in doc.css("input[id='__EVENTVALIDATION']") {
self.eventvalidation=show["value"]!
//print(show["value"] as Any)
}
}
}
//creating dictionary for parameters
self.parameter = ["__EVENTTARGET":"",
"__EVENTARGUMENT":"",
"__LASTFOCUS":"",
"__VIEWSTATE":self.viewstate,
"__EVENTVALIDATION":self.eventvalidation,
"rdolst":"S",
"Txtstudid":"<myrollno>",
"TxtPasswd":"<mypassword>",
"btnlogin":"Login"
]
}
Alamofire.request ("https://ecampus.psgtech.ac.in/studzone/AttWfLoginPage.aspx",method: .post, parameters: self.parameter, headers: headers).responseString { response in
print("\(response.result.isSuccess)")
print(response)
}
To be honest, I'm very new to requests and parsing data(I have finished the parsing part separately though). I did some more research and read about headers and cookies.So after checking the headers, the initial GET request by the browser has a response header of
Cache-Control : private
Content-Encoding : gzip
Content-Length : 4992
Content-Type : text/html; charset=utf-8
Date : Sun, 18 Jun 2017 14:25:50 GMT
Server : Microsoft-IIS/8.0
Set-Cookie : .ASPXAUTH=; expires=Mon, 11-Oct-1999 18:30:00 GMT; path=/; HttpOnly
Vary : Accept-Encoding
X-AspNet-Version : 4.0.30319
X-Powered-By : ASP.NET
and Request Header of
Accept : text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8
Accept-Encoding : gzip, deflate, br
Accept-Language : en-US,en;q=0.5
Connection : keep-alive
Cookie : ASP.NET_SessionId=urzugt0zliwkmz3ab1fxx1ja
Host : ecampus.psgtech.ac.in
Upgrade-Insecure-Requests : 1
User-Agent : Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0`
The problem is I don't understand how a initial GET request can have a token with it. If request happens first, response should be the one containing the token? I don't know what I am doing wrong and how to get this working. I don't know if I am missing something altogether. I came here only after trying everything I could think of. Any help would be appreciated. Thank you.

EVENTVALIDATON and VIEWSTATE parameters required for the POST <--
But in your code the POST request is executed immediately after the GET request, at this point the self.parameter is empty
Alamofire has asynchronous completionHandler
Wait for the GET request to complete, and then send the POST request:
var parameter: Parameters = [:]
var viewstate: String = ""
var eventvalidation: String =
#IBAction func postRequest(_ sender: Any) {
Alamofire.request("https://ecampus.psgtech.ac.in/studzone/AttWfLoginPage.aspx").responseString { response in
print("\(response.result.isSuccess)")
if let html = response.result.value {
if let doc = Kanna.HTML(html: html, encoding: String.Encoding.utf8) {
// Search for nodes by CSS selector
for show in doc.css("input[id='__VIEWSTATE']") {
self.viewstate=show["value"]!
//print(show["value"] as Any)
}
for show in doc.css("input[id='__EVENTVALIDATION']") {
self.eventvalidation=show["value"]!
//print(show["value"] as Any)
}
}
}
//creating dictionary for parameters
self.parameter = ["__EVENTTARGET":"",
"__EVENTARGUMENT":"",
"__LASTFOCUS":"",
"__VIEWSTATE":self.viewstate,
"__EVENTVALIDATION":self.eventvalidation,
"rdolst":"S",
"Txtstudid":"15i231",
"TxtPasswd":"OpenSesame",
"btnlogin":"Login"
]
//Wait for the GET request to complete, and then send the POST request: <<==
Alamofire.request ("https://ecampus.psgtech.ac.in/studzone/AttWfLoginPage.aspx",method: .post, parameters: self.parameter, headers: headers).responseString { response in
print("\(response.result.isSuccess)")
print(response)
}
}

Related

Handle non-JSON response from POST request with Alamofire

I'm using Alamofire 3 and the end point I'm calling (I don't own the server) accepts a POST request and returns HTML as a response.
I am able to get the HTML response when using curl in the command line, however Alamofire doesn't return the response body, but only the header.
This is my code:
let headers = [
"Referer": "SOMEURL"
]
Alamofire.request(.POST, url, headers: headers)
.validate()
.response { request, response, data, error in
// do something with response
}
response is:
Optional(<NSHTTPURLResponse: 0x7f9ba3759760> { URL: SOMEURL } { status code: 200, headers {
Connection = "keep-alive";
"Content-Encoding" = gzip;
"Content-Type" = "text/html";
Date = "Thu, 22 Sep 2016 15:19:20 GMT";
Server = nginx;
"Transfer-Encoding" = Identity;
Vary = "Accept-Encoding";
} })
and data is:
Optional<NSData>
- Some : <>
Any thoughts?

Swift - Server response "unsupported_grant_type"

I'm trying to login using alamofire.
im using the following code:
let parameters = [
"username": "2gggggjggg",
"password": "2ubgh",
]
Alamofire.request(.POST, URL , parameters: parameters, encoding: .JSON).responseJSON { response in
print("request")
print(response.request) // original URL request
print("response")
print(response.response) // URL response
print("data")
print(response.data) // server data
print("result")
print(response.result) // result of response serialization
print("JSON")
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
if let myData = response.data?.base64EncodedStringWithOptions(.Encoding64CharacterLineLength) {
SignUpVC.clientID = myData
print(myData)
}
}
the server response as follows:
data
Optional(<7b226572 726f7222 3a202275 6e737570 706f7274 65645f67 72616e74 5f747970 65227d>)
result
SUCCESS
JSON
JSON: {
error = "unsupported_grant_type";
}
what might the reason be? am i missing anything?
im new to implementing apps that connects to a server.
as far as i know the server uses Django with REST framework and oAuth2.
i hope to get enough help about it. I've tried to search a lot about this problem and how to resolve it but no luck.
thanks in advance.
Debugging check list
Did you use https in your url?
Did your server have Server Certificate from Certification authority Note iOS 9 will not connected to self sign
Certificate)?
Do you use object maper like "SwiftyJSON" object Model to json
not from the error massage I think you have to check your post url or you Server Certificate
let URL:String ="https://youURLHere"

How to prevent EVURLCache to cache a request with Cache-Control header?

I'm using EVURLCache and the POST request response is cached even if cache-control and Pragma headers are defined :
Headers: [
Access-Control-Allow-Headers : Content-Type, Accept, X-Requested-With, remember-me, x-auth-token
Content-Type : application/json;charset=UTF-8
Access-Control-Max-Age : 3600
Cache-Control : no-cache, no-store, max-age=0, must-revalidate
Server : nginx/1.9.15
Connection : keep-alive
Transfer-Encoding : Identity
X-XSS-Protection : 1; mode=block
X-Content-Type-Options : nosniff
Expires : 0
X-Application-Context : application:dev,docker:8080
Pragma : no-cache
Access-Control-Allow-Methods : POST, GET, OPTIONS, DELETE, PUT
Date : Thu, 18 Aug 2016 13:57:15 GMT
Access-Control-Allow-Origin : *
Access-Control-Allow-Credentials : true
]
How to force EVURLCache not to cache such a request ?
I'm initializing cache in my app delegate using the following code:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// configuring cache
EVURLCache.LOGGING = true // We want to see all caching actions
EVURLCache.activate()
return true
}
Note that I know filters but I wonder if we can just tell it to follow the response header suggested cache behavior
I have created an update (not yet pushed to GitHub) and would like to have your input. The code for the fix for this is:
var shouldSkipCache: String? = nil
// check if caching is allowed according to the request
if request.cachePolicy == NSURLRequestCachePolicy.ReloadIgnoringCacheData {
shouldSkipCache = "request cache policy"
}
// check if caching is allowed according to the response Cache-Control or Pragma header
if let httpResponse = cachedResponse.response as? NSHTTPURLResponse {
if let cacheControl = httpResponse.allHeaderFields["Cache-Control"] as? String {
if cacheControl.lowercaseString.containsString("no-cache") || cacheControl.lowercaseString.containsString("no-store") {
shouldSkipCache = "response cache control"
}
}
if let cacheControl = httpResponse.allHeaderFields["Pragma"] as? String {
if cacheControl.lowercaseString.containsString("no-cache") {
shouldSkipCache = "response pragma"
}
}
}
if shouldSkipCache != nil {
// If the file is in the PreCache folder, then we do want to save a copy in case we are without internet connection
let storagePath = EVURLCache.storagePathForRequest(request, rootPath: EVURLCache._preCacheDirectory) ?? ""
if !NSFileManager.defaultManager().fileExistsAtPath(storagePath) {
EVURLCache.debugLog("CACHE not storing file, it's not allowed by the \(shouldSkipCache) : \(request.URL)")
return
}
EVURLCache.debugLog("CACHE file in PreCache folder, overriding \(shouldSkipCache) : \(request.URL)")
}
In short this means that:
If the request was created while indicating that cached data should beignored then the response will not be written to the cache
If the response has a Cache-Control header that contains no-cache or no-store then the response will not be written to the cache
If the response has a Pragma header that contains no-cache then the response will not be written to the cache
All above will be ignored if the file is already in the cache. (then at least it should be updated)

Invalid Token When Using Alamofire

Environment: Swift 2, iOS 9
I had rolled my own HTTP networking library. It wasn't broken but I decided to fix it and switch to Alamofire.
Here is my code snippet:
case .HTTPTokenAuth: // Token Auth
let dictionary = Locksmith.loadDataForUserAccount("Auth_Token", inService: "KeyChainService")
let token = dictionary!.valueForKey("access_token")
var aManager = Manager.sharedInstance
aManager.session.configuration.HTTPAdditionalHeaders = ["Authorization": "Bearer \(token!)" ]
aManager.request(method, requestURL!, parameters: parameters, encoding: .JSON).response { request, response, data, error in
print("===== Request ================")
print(request!.allHTTPHeaderFields!)
print("===== Response ================")
print(response!.allHeaderFields)
print("===== Error ================")
print(error)
}
The problem is that for some reason the Oauth token is not making it into the header. (At least that is what appears to be the problem as I don't see it in the Request header. I receive the following request/response:
===== Request ================
["Content-Type": "application/json"]
===== Response ================
[Content-Type: application/json,
Www-Authenticate: Bearer realm="Doorkeeper",
error="invalid_token",
error_description="The access token is invalid",
Pragma: no-cache, X-Runtime: 0.002205,
X-Powered-By: Phusion Passenger 4.0.14, X-XSS-Protection: 1; mode=block, Server: nginx/1.6.2 + Phusion Passenger 4.0.14,
Transfer-Encoding: Identity,
Cache-Control: no-store,
Date: Tue, 01 Sep 2015 18:54:16 GMT,
X-Request-Id: 395d2154-5054-423c-a7bc-f7ef85e0cdbf,
Connection: keep-alive,
X-Content-Type-Options: nosniff,
X-UA-Compatible: chrome=1,
Status: 401 Unauthorized,
X-Frame-Options: SAMEORIGIN]
I went with the simpler approach:
case .HTTPTokenAuth: // Token Auth
let dictionary = Locksmith.loadDataForUserAccount("Auth_Token", inService: "KeyChainService")
let token = dictionary!.valueForKey("access_token")
let headers = ["Authorization":"Bearer \(token!)"]
Alamofire.request(method, requestURL!, parameters: parameters, headers: headers, encoding: .JSON)
.response { request, response, data, error in
print(request!.allHTTPHeaderFields!)
print(response)
print(error)
}

Office 365 iOS SDK - How to invoke SharePoint REST API

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!
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! :-)

Resources