Swift WKWebView - ntlm

how to handle NTLM authentication using WKWebView, the aim is to load a secured URL with credentials (Sharepoint Hosted application) similarly I wanted to add a key value pair to the http request
any code sample using swift2.0 that handles my case will be appreciated .

I went through the documentation and I have found the solutions below is the equivalent of the method shouldSatratLoading etc..
func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {
print(" decidePolicyForNavigationAction.......")
let headerArr = navigationAction.request.allHTTPHeaderFields?.keys.array
let headerIsPresent = headerArr?.contains(APP_HEADER_ID)
if headerIsPresent! {
decisionHandler(.Allow)
}else{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
dispatch_async(dispatch_get_main_queue(), {
let newRequest: NSMutableURLRequest = navigationAction.request as! NSMutableURLRequest
// set new header
newRequest.addValue(APP_HEADER_VALUE, forHTTPHeaderField:APP_HEADER_ID)
// reload the request
webView.loadRequest(newRequest)
})
})
decisionHandler(.Cancel)
}
}
If anyone needs more info please let me know

Related

Tauri Redirect URI Schema

I'm building a Tauri app and would like to set up OAuth integration with Google. To do so, I will need a URI for the oauth callback, but Tauri is unclear how to configure the schema possibly using this method or with the WindowUrl?
How can I add a URI to my Tauri app so I could like to it like the following example:
myapp://callback
I think it could look something like the following:
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.register_uri_scheme_protocol("myapp", move |app, request| {
# protocol logic here
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Tauri currently doesn't directly support deep linking. A good alternative that I found was this rust project. After installation you can do something like the following:
#[tauri::command]
async fn start_oauth_server(window: Window) -> Result<u16, String> {
println!("Starting server");
start(None, move |url| {
// Because of the unprotected localhost port, you must verify the URL here.
// Preferebly send back only the token, or nothing at all if you can handle everything else in Rust.
// convert the string to a url
let url = url::Url::parse(&url).unwrap();
// get the code query parameter
let code = url
.query_pairs()
.find(|(k, _)| k == "code")
.unwrap_or_default()
.1;
// get the state query parameter
let state = url
.query_pairs()
.find(|(k, _)| k == "state")
.unwrap_or_default()
.1;
// create map of query parameters
let mut query_params = HashMap::new();
query_params.insert("code".to_string(), code.to_string());
query_params.insert("state".to_string(), state.to_string());
query_params.insert(String::from("redirect_uri"), url.to_string());
if window.emit("redirect_uri", query_params).is_ok() {
println!("Sent redirect_uri event");
} else {
println!("Failed to send redirect_uri event");
}
})
.map_err(|err| err.to_string())
}

SwiftUI: How to add CoreData record from Siri Intent

I am trying to create an Intent that saves a record to a CoreData database. The record will be created if I run the code from the main app, but not in the Intent.
Here is the code:
import Intents
import CoreData
import SwiftUI
let persistenceController = PersistenceController.shared
class IntentHandler: INExtension, DiaryIntentHandling
{
var moc = PersistenceController.shared.context
override func handler(for intent: INIntent) -> Any?
{
guard intent is DiaryIntent else
{
fatalError("Unknwonwn intent type: \(intent)")
}
return self
}
func handle(intent: DiaryIntent, completion: #escaping (DiaryIntentResponse) -> Void)
{
guard let message = message
else
{
completion(DiaryIntentResponse(code: .failure, userActivity: nil))
return
}
completion(DiaryIntentResponse.success(message: message))
let context = PersistenceController.shared.container.viewContext
let myRecord = MyRecord(context: context)
myRecord.timestamp = Date()
myRecord.message = message
do {
try context.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
func resolveMessage(for intent: DiaryIntent, with completion: #escaping (INStringResolutionResult) -> Void)
{
if let message = intent.message
{
completion(INStringResolutionResult.success(with: message))
}
else
{
completion(INStringResolutionResult.needsValue())
}
}
public func confirm(intent: DiaryIntent, completion: #escaping (DiaryIntentResponse) -> Void) {
completion(DiaryIntentResponse(code: .ready, userActivity: nil))
}
}
Do I need to share access to the CoreData database? How do I create the record?
App extensions work like separate apps, so you need to set up an app "group" to share data between them. It gives you a directory that's not part of your app's sandbox that your app and your app extensions can share. Using one requires some setup work:
Turn on app groups by adding the group entitlement. Apple has some documentation on this. I also have a somewhat old blog post that's still accurate as far as setting up the group.
Set up your persistent container use the group directory for Core Data. Normally it saves data in your app's sandbox, but you can tell it to use the app group directory. To do that,
Get a file URL for the directory using FileManager's function containerURL(forSecurityApplicationGroupIdentifier:). The argument is the same as your app group identifier.
Make sure this directory exists! It doesn't get created automatically. Use FileManager.default.fileExists(atPath:) to check if it exists, and if not, use FileManager.default.createDirectory(at:withIntermediateDirectories:attributes:) to create it.
Use a NSPersistentStoreDescription to tell your persistent container to use that URL for Core Data. That would be something like
let persistentContainer = NSPersistentContainer(name: containerName)
let persistentStoreDescription = NSPersistentStoreDescription(url: persistentStoreUrl)
persistentStoreDescription.type = NSSQLiteStoreType
persistentContainer.persistentStoreDescriptions = [ persistentStoreDescription ]
After the previous step, the persistent container won't be able to find any data that's currently in Core Data. So:
If your app has not already been released, delete it from your test devices and simulators and then rebuild. You'll get a new persistent store in the app group directory.
If your app has already been released, add code to copy the persistent store from the current location to the new location. Do this before loading the persistent store, and only do it if the copy in the app group doesn't already exist (so you don't re-copy old data). The best way to do that is with the migratePersistentStore(_:to:options:withType:) function from NSPersistentStoreCoordinator. Don't just copy your SQLite file over, because that won't include all the data.

How to get back to app after google login

I'm trying to implement google login in my app using xamarin.auth like below
var auth = new OAuth2Authenticator("284202576320-7kgdhaa5sgvkoe03jmmcv0p8lfdma306.apps.googleusercontent.com","cAZW7uegD-h2-
tNMMf5q1UGQ","https://www.googleapis.com/auth/userinfo.email",new
Uri("https://accounts.google.com/o/oauth2/auth"),new
Uri("http://dev.myfav.restaurant/Account/LoginComplete"),new
Uri("https://accounts.google.com/o/oauth2/token"),null,true)
{
AllowCancel = true,
};
but Completed event not firing and its going to web page after login :(
I'm getting below error
i need to get back user to my app how can i achieve this ???? Can anyone help me on this please.
Thanks in advance
Hey follow these two examples one is using web view and one is using google sign in sdk for google auth.
https://timothelariviere.com/2017/09/01/authenticate-users-through-google-with-xamarin-auth/
and
https://developer.xamarin.com/samples/xamarin-forms/WebServices/OAuthNativeFlow/
So according to this issue reported by Mounika.Kola .I think u should refer that authenticator.Completed -= OnAuthCompleted is there in ur code. For reference just see these codes which i used for google authorization in Xamarin Forms.
void OnLoginClicked(object sender, EventArgs e)
{
string clientId = null;
string redirectUri = null;
switch (Device.RuntimePlatform)
{
case Device.iOS:
clientId = Constants.iOSClientId;
redirectUri = Constants.iOSRedirectUrl;
break;
case Device.Android:
clientId = Constants.AndroidClientId;
redirectUri = Constants.AndroidRedirectUrl;
break;
}
var authenticator = new OAuth2Authenticator(
clientId,
null,
Constants.Scope,
new Uri(Constants.AuthorizeUrl),
new Uri(redirectUri),
new Uri(Constants.AccessTokenUrl),
null,
true);
authenticator.Completed += OnAuthCompleted;
authenticator.Error += OnAuthError;
AuthenticationState.Authenticator = authenticator;
var presenter = new Xamarin.Auth.Presenters.OAuthLoginPresenter();
presenter.Login(authenticator);
}
async void OnAuthCompleted(object sender, AuthenticatorCompletedEventArgs e)
{
var authenticator = sender as OAuth2Authenticator;
if (authenticator != null)
{
authenticator.Completed -= OnAuthCompleted;
authenticator.Error -= OnAuthError;
}
User user = null;
if (e.IsAuthenticated)
{
// If the user is authenticated, request their basic user data from Google
// UserInfoUrl = https://www.googleapis.com/oauth2/v2/userinfo
var request = new OAuth2Request("GET", new Uri(Constants.UserInfoUrl), null, e.Account);
var response = await request.GetResponseAsync();
if (response != null)
{
// Deserialize the data and store it in the account store
// The users email address will be used to identify data in SimpleDB
string userJson = await response.GetResponseTextAsync();
user = JsonConvert.DeserializeObject<User>(userJson);
}
if (account != null)
{
store.Delete(account, Constants.AppName);
}
await store.SaveAsync(account = e.Account, Constants.AppName);
await DisplayAlert("Email address", user.Email, "OK");
}
}
I hope it helps you.
In iOS once you have completed the authentication with Xamarin.Auth you just need to dismiss the viewController and you will be put back in your app.
You do this subscribing to the Completed event of the OAuth2Authenticator
auth.Completed += (sender, e) =>
{
DismissViewController(true, null);
};
If the "Native UI" is used (the last parameter in the constructor is set to true), which means that external/system browser is used for login not WebView. So, on Android instead of WebView [Chrome] CustomTabs is used and on iOS instead of UIWebView (or WKWebView) SFSafariViewController is used.
With native UI user is leaving your app and the only way to return to your app is app-linking (or deep-linking) and this requires completely different approach.
1st you cannot use http[s] scheme for redirect_url (OK on Android it is possible, but on iOS not). Use custom scheme for that.
See the sample[s] (Xamarin.Forms ComicBook):
https://github.com/moljac/Xamarin.Auth.Samples.NugetReferences
And the docs in the repo:
https://github.com/xamarin/Xamarin.Auth/tree/master/docs

How to generate iOS Application Certificate for Fairplay implementation

We are developing a iOS music app. For content protection we are going to use Apples fairplay DRM system. I am following apple's HDLCatalog example for reference. While implementing i noticed there are two methods in AssetLoaderDelegate class that need to be implemented. I will appreciate if any one can help me out how to implemented below two methods. Thanks in advance.
1.)
public func fetchApplicationCertificate() -> Data? {
// MARK: ADAPT: YOU MUST IMPLEMENT THIS METHOD.
let applicationCertificate: Data? = nil
if applicationCertificate == nil {
fatalError("No certificate being returned by \(#function)!")
}
return applicationCertificate
}
2.)
public func contentKeyFromKeyServerModuleWithSPCData(spcData: Data, assetIDString: String) -> Data? {
// MARK: ADAPT: YOU MUST IMPLEMENT THIS METHOD.
let ckcData: Data? = nil
if ckcData == nil {
fatalError("No CKC being returned by \(#function)!")
}
return ckcData
}
I am updating here that we managed to implement fetchApplicationCertificate() method. Now we are facing problems for generating ckc data
Application Certificate
The application certificate is the DER formated public certificate created when you registered for Fairplay with Apple. This should be placed on a web server (AWS S3 is ideal) and retrieved once per application session.
CKC Data
This is specific to whomever you're using for Fairplay License Services. They will have specified an interface for sending the SPC data from your client to their license server. This could be JSON over REST, SOAP, MQ or anything they chose. You will have to ask them for the API spec.
Step 1 :
let queue = DispatchQueue(label: "fairplay.resourcerequests", attributes: [])
let url = URL(string: videoUrl)! // Streaming the video from this URL
let videoAsset = AVURLAsset(url: url, options: nil)
videoAsset.resourceLoader.setDelegate(self, queue: queue)
Step 2:
extension PlayerViewController : AVAssetResourceLoaderDelegate{
func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
let url = loadingRequest.request.url
var error: NSError?
print("Player Delegate Method")
print("URL Schema Is \(url?.scheme! ?? "")")
if((url?.scheme ?? "") == "skd"),
let assetString = url!.host, let assetID = assetString.data(using: String.Encoding.utf8) // Get the URI for the content key.{guard let fetchedCertificate = self.fetchedCertificate else { return false} // Get the application certificate from the server.
let requestedBytes = try loadingRequest.streamingContentKeyRequestData(forApp: fetchedCertificate, contentIdentifier: assetID, options: nil)
do{
print("Online Video Streaming Going On")
let responseData = try contentKeyFromKeyServerModuleWithRequestData(requestedBytes, assetString: assetString, expiryDuration: expiryDuration)
guard let dataRequest = loadingRequest.dataRequest else {
// print("Failed to get instance of AVAssetResourceLoadingDataRequest (loadingRequest.dataRequest).")
return false
}
dataRequest.respond(with: responseData)
if let infoRequest = loadingRequest.contentInformationRequest,
expiryDuration != 0.0
{
infoRequest.renewalDate = Date(timeIntervalSinceNow: expiryDuration)
infoRequest.contentType = "application/octet-stream"
infoRequest.contentLength = Int64(responseData.count)
infoRequest.isByteRangeAccessSupported = false
}
// Treat the processing of the requested resource as complete.
loadingRequest.finishLoading()
// The resource request has been handled regardless of whether the server returned an error.
return true
}catch let e as NSError
{
error = e
// print("content key error\(error)")
}
}catch let e as NSError {
error = e
// Resource loading failed with an error.
// print("streamingContentKeyRequestDataForApp failure: \(error.localizedDescription)")
}}}
Step 3:
func contentKeyFromKeyServerModuleWithRequestData(_ requestBytes: Data, assetString: String, expiryDuration: TimeInterval?=0.0, persitent:Bool?=true) throws -> Data {
// If the key server provided a CKC, return it.
// if let ckcData = ckcData {
// return ckcData
// }
// else
// {
let base64Decoded = requestBytes.base64EncodedString(options: NSData.Base64EncodingOptions())
//MARK: Get Fairplay license for the current user
NSLog("using ticket: %#", streamTicket )
if let returnData:Data = mediaMakerDRMLicenseCall(base64Decoded, ticket: streamTicket)
{
if returnData.count <= 0
{
// Otherwise, the CKC was not provided by key server. Fail with bogus error.
// Generate an error describing the failure.
throw NSError(domain: "com.example.apple-samplecode", code: 0, userInfo: [
NSLocalizedDescriptionKey: NSLocalizedString("Item cannot be played.", comment: "Item cannot be played."),
NSLocalizedFailureReasonErrorKey: NSLocalizedString("Could not get the content key from the Key Server.", comment: "Failure to successfully send the SPC to the Key Server and get the content key from the returned Content Key Context (CKC) message.")
])
}
else
{
return returnData
}
}
//}
}
Step 4:
func mediaMakerDRMLicenseCall(_ playerSPC : String, ticket : String) -> Data{// API Call to fetch license from client server}

example of parsing a receipt for an in-app purchase using iOS Xamarin?

I am trying to implement purchase validation for my app. I see that I can send the purchase receipt to my server to verify with Apple. However, I cannot figure out the correct way to POST the NSData to my URL for validation. Something like this:
public void CompleteTransaction (SKPaymentTransaction transaction) {
var productId = transaction.Payment.ProductIdentifier;
NSUrl receiptURL = NSBundle.MainBundle.AppStoreReceiptUrl;
NSData theData = NSData.FromUrl (receiptURL);
RestRequest request = new RestRequest(validationURL, Method.POST);
request.AddBody(theData); // ??
restClient.ExecuteAsync<bool>((response) =>
{
FinishTransaction(transaction, response.Data);
});
}
Does anyone have an example? I am using RestSharp.
Thanks!
davevr
OK, found how to do it. The trick was to parse the receipt into a dictionary and then pull the key out of that. Sample code:
public void CompleteTransaction (SKPaymentTransaction transaction) {
var productId = transaction.Payment.ProductIdentifier;
NSUrl receiptURL = NSBundle.MainBundle.AppStoreReceiptUrl;
NSData receipt = NSData.FromUrl (receiptURL);
// here is the code I was missing
NSDictionary requestContents = NSDictionary.FromObjectAndKey((NSString)receipt.GetBase64EncodedString(
NSDataBase64EncodingOptions.None),
(NSString)"receipt-data");
string receiptData = (requestContents["receipt-data"] as NSString).ToString();
RestRequest request = new RestRequest(<url to your server>, Method.POST);
request.AddParameter ("receipt-data", receiptData );
apiClient.ExecuteAsync<bool>(request, (response) =>
{
FinishTransaction (transaction, response.Data);
});
Once that is done, you can do the validation on the Apple server. There is lots of sample code on the net for that part.

Resources