I have iOS application, that you need to login to, and view some user data...
For some extra edit, I have url link to page, where can user set some special properties,... but it is really annoying for users to login inside page, if they are already logged in application.
For this reason I need to store custom cookies in HTTPCookieStorage, so that when user navigate to the page, authentication cookies would be set for him.
How to create custom cookie with key-value, and store it so that Safari will use it when user navigate to my page?
EDIT
I will provide more information to better understand my situation.
I have one button inside my app.
After clicking to this button, I call API to get valid token.
After receiving token, I need to store this token as cookies. The "server" way of doing this would be
localStorage.setItem('token', this.token)
After I would store this token into HTTPCookieStorage, I will open my page.
UIApplication.shared.open(myURL, options: [:], completionHandler: { (success) in
print("Url open")
})
In page on myURL, the application look for a token cookies. If cookies exist it open web app, else it presents login page.
So my goal is to prevent opening login page.
Store your cookies:
func storeCookies() {
let cookiesStorage = HTTPCookieStorage.shared
let userDefaults = UserDefaults.standard
let serverBaseUrl = "http://example.com"
var cookieDict = [String : AnyObject]()
for cookie in cookiesStorage.cookies(for: NSURL(string: serverBaseUrl)! as URL)! {
cookieDict[cookie.name] = cookie.properties as AnyObject?
}
userDefaults.set(cookieDict, forKey: "cookiesKey")
}
Retrieve:
func restoreCookies() {
let cookiesStorage = HTTPCookieStorage.shared
let userDefaults = UserDefaults.standard
if let cookieDictionary = userDefaults.dictionary(forKey: "cookiesKey") {
for (_, cookieProperties) in cookieDictionary {
if let cookie = HTTPCookie(properties: cookieProperties as! [HTTPCookiePropertyKey : Any] ) {
cookiesStorage.setCookie(cookie)
}
}
}
}
Related
My app has a "Sign in with the Apple" account feature. I am wondering if there is sign out feature from the Apple account.
I tried the below but doesn't get success
let request = ASAuthorizationAppleIDProvider().createRequest()
request.requestedOperation = .operationLogout
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.performRequests()
Apple only allows currently for the user to perform a signout (iOS/watchOS/tvOS) or shown as a revoke of permissions to us. They recommend you get the state of the credentials before use to check for revoke and if that has occurred to delete any local information (Remove the user identifier where ever you have it stored) (And possibly change UI if needed; For example like showing login view).
let appleIDProvider = ASAuthorizationAppleIDProvider()
appleIDProvider.getCredentialState(forUserID: KeychainItem.currentUserIdentifier) { (credentialState, error) in
switch credentialState {
case .authorized:
// The Apple ID credential is valid.
break
case .revoked:
// The Apple ID credential is revoked.
break
case .notFound:
// No credential was found, so show the sign-in UI.
break
default:
break
}
}
You could provide a prompt to the user on signout guiding them to revoke in their device's settings as well and listen for the change notification.
You need to delete the existing item from the keychain.
This is my sample code, using Apple sample code.
You can get sample code from Apple
Apple recommend you get the state of the credentials before use to check for revoke and if that has occurred to delete any local information
struct KeychainItem {
init(service: String, account: String, accessGroup: String? = nil) {
self.service = service
self.account = account
self.accessGroup = accessGroup
}
static func deleteUserIdentifierFromKeychain() {
do { //please change service id to your bundle ID
try KeychainItem(service: "com.example.apple-samplecode", account: "userIdentifier").deleteItem()
} catch {
print("Unable to delete userIdentifier from keychain")
}
}
func deleteItem() throws {
// Delete the existing item from the keychain.
let query = KeychainItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup)
let status = SecItemDelete(query as CFDictionary)
// Throw an error if an unexpected status was returned.
guard status == noErr || status == errSecItemNotFound else { throw KeychainError.unhandledError }
}
keychainQuery
keychainQuery is from apple sample code.
private static func keychainQuery(withService service: String, account: String? = nil, accessGroup: String? = nil) -> [String: AnyObject] {
var query = [String: AnyObject]()
query[kSecClass as String] = kSecClassGenericPassword
query[kSecAttrService as String] = service as AnyObject?
if let account = account {
query[kSecAttrAccount as String] = account as AnyObject?
}
if let accessGroup = accessGroup {
query[kSecAttrAccessGroup as String] = accessGroup as AnyObject?
}
return query
}
I have followed some tutorials and used their methods to implement auto login for my app, but once I relaunch the app after entering the credentials, the app does not log in.
var userDefaults = UserDefaults.standard
here I initiate the user defaults feature
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let safeData = data {
if let dataString = String(data: safeData, encoding: String.Encoding.utf8) {
print(dataString)
if dataString == "Hello" {
self.userDefaults.setValue(true, forKey: "UserIsLoggedIn")
DispatchQueue.main.async {
self.performSegue(withIdentifier: "loginSegue", sender: self)
}
} else {
DispatchQueue.main.async {
self.validationLabel.isHidden = false
self.validationLabel.text = " Username or password is incorrect. "
self.loginSuccessful = false
}
}
}
} else {
print(error ?? "Error with data API URLSession")
}
}.resume()
here, inside the API call. if the response from the API is "hello" which means the login was successful, i set the value to true with an identifier.
if userDefaults.value(forKey: "UserIsLoggedIn") as? Bool == true {
performSegue(withIdentifier: "loginSegue", sender: self)
} else {}
here in the view did load I use the userDefaults to perform the segue to the next screen for future launches.. but it is not working.
initiation
viewdidload
API call
var userDefaults = UserDefaults() seems wrong. Use let userDefaults = UserDefaults.standard instead. Also there's no need to make this a property of your class, simply use this within your methods whereever it's needed.
Swift 5. Auto login myApplication According woking & help you best my try
//First Time key
// According API Response success then add
if !UserDefaults.standard.bool(forKey:"isLogin") { // Success
UserDefaults.standard.set(true, forKey: "isLogin") // To do....
UserDefaults.standard.synchronize()
} else { // response fail
// To do ....
}
Have you checked if the value is being saved in your User Defaults file? If you are running your app on the simulator, try printing the file path to your User Defaults file and try locating it. It might not fix your immediate problem but it will hopefully give you an idea as to where the problem is coming from.
Try printing the location of your User Defaults file using the following line in your AppDelegate.swift
print(NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last! as String)
I have a login to an API which works and now want to load a web view which needs the authentication token. I'm testing my app using the Xcode Simulator for iOS 10.1.
When I load the web view I get a sign in page and a message from the API "It looks like you are not accepting cookies". Is this a case of mismatched cookies names expected or something I have missed?
func createWebViewRequest() {
self.createCookie()
let url = URL (string: "myDomain/info/wishlists")
let requestObj = NSMutableURLRequest(url: url!)
requestObj.httpShouldHandleCookies = true
let headers = HTTPCookie.requestHeaderFields(with: HTTPCookieStorage.shared.cookies!)
requestObj.allHTTPHeaderFields = headers
requestObj.addValue("iOS", forHTTPHeaderField: "X-My-Client")
self.webview!.loadRequest(requestObj as URLRequest)
self.webview!.delegate = self
self.view.addSubview(self.webview!)
}
func createCookie() {
let infoModel = UserInfoManager.getUserInfoModel()
if infoModel != nil {
let cookieProps = NSMutableDictionary()
cookieProps.setValue("NIDDEV", forKey: HTTPCookiePropertyKey.name.rawValue)
cookieProps.setValue(infoModel?.userAccessToken, forKey: HTTPCookiePropertyKey.value.rawValue)
cookieProps.setValue(".mydomain.com", forKey: HTTPCookiePropertyKey.domain.rawValue)
cookieProps.setValue("/", forKey: HTTPCookiePropertyKey.path.rawValue)
cookieProps.setValue("0", forKey: HTTPCookiePropertyKey.version.rawValue)
cookieProps.setValue(Date().addingTimeInterval(31536000), forKey: HTTPCookiePropertyKey.expires.rawValue)
let dict:NSDictionary = cookieProps as NSDictionary
let cookie = HTTPCookie(properties: dict as! [HTTPCookiePropertyKey : Any])
HTTPCookieStorage.shared.setCookie(cookie!)
}
else {
let delegate = UIApplication.shared.delegate as! AppDelegate
delegate.callAuthTokenWebservice()
}
}
I dumped the create cookie method above and decided to save the cookies using the response header for the immediately prior check auth call which comes from an API and authenticates in the header. This stopped the target web site from complaining about not getting cookies but it still wouldn't accept the auth key.
private func setCookies(response: URLResponse) {
if let httpResponse = response as? HTTPURLResponse {
if let headerFields = httpResponse.allHeaderFields as? [String: String] {
let _ = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: response.url!)
// print(cookies)
}
}
}
I want to create a iOS App with 2 Webviews (1 Shop, 1 Logged-In Shop)
If I login in one webview, the other webview includes the same cookies and the first webview is logged in too.
I need the first one to have it's own session (cookies)!
My plan is to have a saved login Touch-ID protected and a free everyone can use webview.
I was currently searching for 2 and a half hours and tried stuff like:
let request = NSMutableURLRequest(URL: NSURL(string: "https://www.my-url.com")!)
request.setValue("", forHTTPHeaderField: "Cookie")
webView.loadRequest(request)
Yes , you can save your cookies to the first webview from webViewDidFinishLoad:
let storage = NSHTTPCookieStorage.sharedHTTPCookieStorage()
for cookie in storage.cookies as! [NSHTTPCookie]{
print(cookie)
}
NSUserDefaults.standardUserDefaults().synchronize()
If you set to the second webview to the loadRequest:
var req = NSURLRequest(URL: NSURL(string: urlString)!)
var storage = NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies as! [NSHTTPCookie]
var reqCookies:[AnyObject] = []
for aCookie in storage {
reqCookies += [aCookie]
}
var headers = NSHTTPCookie.requestHeaderFieldsWithCookies(reqCookies)
self.webView.loadRequest(req)
Otherwise you can delete:
let storage = NSHTTPCookieStorage.sharedHTTPCookieStorage()
for cookie in storage.cookies! {
storage.deleteCookie(cookie)
}
NSUserDefaults.standardUserDefaults().synchronize()
EDIT (after comments): to manage different sessions you can look in this Stackoverflow answer to handle your different sessions.
I've integrated login with vk button in my ios app. And I want to add an ability to switch account.
I've tried to run network request to http://api.vk.com/oauth/logout. But it outputs wrong logout hash.
I used this code:
let logoutUrl = "http://api.vk.com/oauth/logout"
let request = NSMutableURLRequest(URL: NSURL(string: logoutUrl)!,
cachePolicy:.ReloadIgnoringLocalCacheData,
timeoutInterval:60.0)
let responseData = try! NSURLConnection.sendSynchronousRequest(request, returningResponse: nil)
Also I tried to clear NSDefaults, after logout:
let defaults = NSUserDefaults.standardUserDefaults()
defaults.removeObjectForKey("VKAccessUserId")
defaults.removeObjectForKey("VKAccessToken")
defaults.removeObjectForKey("VKAccessTokenDate")
defaults.synchronize()
And to clear cookies:
let storage = NSHTTPCookieStorage.sharedHTTPCookieStorage()
for cookie in storage.cookies {
let domainName = cookie.domain
let domainRange = domainName.rangeOfString("vk.com")
if(domainRange.length > 0) {
storage.deleteCookie(cookie)
}
}
And nothing helps
I found the solution. Should call VKSdk.forceLogout()