How to use authentication method to access Laravel API with swift? - ios

I'm developing iOS App
I made POST API my self.
Now, when I press the button from iOS, I create a function to access APi and post value.
I tried to input the value to test the API once with PostMan, but I got an error. The page has expired due to inactivity.
I understand that this error is because POST does not include csrf_token.
If you use csrf_token on iOS to authenticate, what is the way to authenticate? Also, are there other authentication methods?
Swift Button function
#IBAction func bookmarkBtn(_ sender: Any) {
let user_id = defaultValues.string(forKey: "id")
let urlString = "http://127.0.0.1:8000/store/favorite"
let request = NSMutableURLRequest(url: URL(string: urlString)!)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let params:[String:Any] = [
"user_id": user_id,
"store_id" : store_id,
]
do{
request.httpBody = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
let task:URLSessionDataTask = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: {(data,response,error) -> Void in
let resultData = String(data: data!, encoding: .utf8)!
print("result:\(resultData)")
print("response:\(response)")
})
task.resume()
}catch{
print("Error:\(error)")
return
}
Laravel FavoriteController
public function favorite(Request $request){
Favorite::create(
array(
'user_id' => $request->user_id,
'store_id' => $request->store_id,
)
);
return ['Status' => 'Success'];
}
Laravel routes/web.php
Route::post('/store/favorite', 'FavoriteController#favorite');

Excluding URIs From CSRF Protection
Sometimes you may wish to exclude a set of URIs from CSRF protection. For example, if you are using Stripe to process payments and are utilizing their webhook system, you will need to exclude your Stripe webhook handler route from CSRF protection since Stripe will not know what CSRF token to send to your routes.
Typically, you should place these kinds of routes outside of the web middleware group that the RouteServiceProvider applies to all routes in the routes/web.php file. However, you may also exclude the routes by adding their URIs to the $except property of the VerifyCsrfToken middleware:
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'stripe/*',
'http://example.com/foo/bar',
'http://example.com/foo/*',
];
}
Note:Apis should be stateless .Better use jwt for authentication and also csrf token only for web interface not for apis
Ref:https://laravel.com/docs/5.6/csrf#csrf-excluding-uris
if you are using latest version laravel then you have api.php instead of web.php for apis
if you still want to use web.php then you can exclude csrf token in VerifyCsrfToken
protected $except = [
'/*',
];
I recommend you add your routes in routes/api.php so that you dont get csrf token issue.Also you need to add api in your urls
http://localhost:8080/api/yourroutename

Related

CreateUploadSession for OneDrive upload using Microsoft Graph API: The request is malformed or incorrect

Using MSAL 1.1.24 & making API calls in an iOS app that supports uploading to OneDrive for a year now.
Some users reported that they sometimes (not 100% of the time) see their upload fail.
The error message is "The request is malformed or incorrect".
Attached is a screenshot with the full error message returned by the servers:
Whats is wrong in the URL?
This is how I create the request:
/* REQUEST */
guard let validPathForURL = uploadPath.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed),
let url = URL(string: "\(ODManager.kGraphEndpoint)"+validPathForURL+":/createUploadSession") else {
DLog("Invalid URL")
completion(QSTransferResult.failure(QSTransferError.ResourceNotFound), nil)
return
}
var request = ODManager.shared.createURLRequestWithToken(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
// Conflict management
let fileExistBehavior = fileExistProcedure == .keepBoth ? "rename" : "replace"
let params = ["item": [
"#microsoft.graph.conflictBehavior":fileExistBehavior,
"name":fileName],
"fileSize":fileSize,
] as [String : Any] // name must be the same as the one mentioned in the URL (in other words, the file name must be in both place)
request.httpBody = try! JSONSerialization.data(withJSONObject: params, options: JSONSerialization.WritingOptions())
The server returning the issue:
I'm going to take a stab at this... I think when it's failing the #name.conflictBehavior annotation is NOT the first property in the JSON document provided to the service (it doesn't actually have to be first - it can be after another instance annotations, but NOT properties). Now the fact that it doesn't happen often is a little odd... is it possible the annotation is only added on occasion (e.g. when a conflict is detected you pop up UI to ask if the user wants to overwrite)?

400 error when downloading file with "Authorization" header

The server returning a json file that is:
{"ctrl":{"code":400,"text":"Not valid Access token","ts":"2020-03-05T11:54:01.547Z"}}
Code:
public func startDownload(url: URL, pathURL: URL) {
let accessToken: String! = "Bearer \(Constants.access_token)"
self.dirURL = pathURL
var request = URLRequest(url: url)
guard let token = accessToken else { return }
request.addValue(token, forHTTPHeaderField: "Authorization")
downloadTask = backgroundSession.downloadTask(with: request)
downloadTask.resume()
}
FYI: access token is valid, it is working with Postman.
You're going to have a problem because, unfortunatelly, there's no good solution to this issue. Authorization is one of the Reserved HTTP Headers and setting it either in URLRequest header, or in URLSessionConfiguration.httpAdditionalHeaders may simply not work:
If you set a value for one of these reserved headers, the system may ignore the value you set, or overwrite it with its own value, or simply not send it.
One might expect you could provide this token in URLSessionTaskDelegate method urlSession(_:task:didReceive:completionHandler:) which handles authentication challenges, but in there you need to provide a URLCredential object, and sadly it doesn't have a constructor that takes a Bearer token, so that's a no-go.
So basically, short of writing your own URLProtocol implementation, your best bet would be to send the token in some additional, custom, header field and have the server grab it from there (if you have control over server code). Source

http request delete and put

Below is my code for HTTP request for getting and post. What I wanted to know is how to do HTTP request for delete and put. I have made it possible for get and post to work. I want to know how on the part of delete and put based on my code below. what to change in my postcode when I want to change it to delete? what is lacking? I wanted to delete using ID
like for example "id": 16,
let parameters = ["name": "test", "desc": "test" , "reward":"1.00" , "sched":"2018-04-05T11:49:51+08:00", "occurrence":["name": "once"]
, "status": "created", "created_by": "test#gmail.com","created_for": "test.com"] as [String : Any]
guard let url = URL(string: "http://test.tesst.eu:8000/api/v1/test/") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []) else { return }
request.httpBody = httpBody
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()
get
guard let url = URL(string: "http://test.test:8000/api/v1/test") else { return }
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()
The only one that differs is GET with the parameters are in the url itself , POST & DELETE & PUT are work the same way , just change the httpMethod parameter and specify httpBody if you wanted to , meaning
DELETE : means delete a resource from a specific url
PUT : place a resource in to a web server
//
let parameters = ["ID": "16"] as [String : Any]
guard let url = URL(string: "http://test.tesst.eu:8000/api/v1/test/") else { return }
var request = URLRequest(url: url)
request.httpMethod = "DELETE"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject:parameters, options: []) else { return }
request.httpBody = httpBody
You should go through difference between each method types. It will help you, what should you do and when.
PUT
Store an entity at a URI. PUT can create a new entity or update an existing one. A PUT request is idempotent. Idempotency is the main difference between the expectations of PUT versus a POST request.
The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI. If a new resource is created, the origin server MUST inform the user agent via the 201 (Created) response. If an existing resource is modified, either the 200 (OK) or 204 (No Content) response codes SHOULD be sent to indicate successful completion of the request. If the resource could not be created or modified with the Request-URI, an appropriate error response SHOULD be given that reflects the nature of the problem. The recipient of the entity MUST NOT ignore any Content-* (e.g. Content-Range) headers that it does not understand or implement and MUST return a 501 (Not Implemented) response in such cases.
Modify the address with an ID of 1:
PUT /addresses/1
Note: PUT replaces an existing entity. If only a subset of data elements are provided, the rest will be replaced with empty or null.
urlRequestInstance.httpMethod = "PUT"
DELETE
Request that a resource be removed; however, the resource does not have to be removed immediately. It could be an asynchronous or long-running request.
The DELETE method requests that the origin server delete the resource identified by the Request-URI. This method MAY be overridden by human intervention (or other means) on the origin server. The client cannot be guaranteed that the operation has been carried out, even if the status code returned from the origin server indicates that the action has been completed successfully. However, the server SHOULD NOT indicate success unless, at the time the response is given, it intends to delete the resource or move it to an inaccessible location.
A successful response SHOULD be 200 (OK) if the response includes an entity describing the status, 202 (Accepted) if the action has not yet been enacted, or 204 (No Content) if the action has been enacted but the response does not include an entity.
Delete an address with an ID of 1:
DELETE /addresses/1
urlRequestInstance.httpMethod = "DELETE"
Here are nice tutorial references for you:
Understanding REST
REST Methods
What is the usefulness of PUT and DELETE HTTP request methods?
Now you can try a sample code, answered by Sh_Khan

Pusher Client Error: Invalid Signature

I recently looked in my pusher error logs and noticed:
Invalid signature: Expected HMAC SHA256 hex digest of
217478.6054950:private-production1_xxxxx_1232:{"user_id":xxxx}, but got 707d39519ca7f971a134524d8fe2ebafbddd64f42b6af0a20d6a73fxxxxxxx
In general our websockets have been working fine. We have many clients working completely fine and sockets in general seem to be working without issue. This is the first time I've noticed this error and I check the error logs fairly frequently. Is this something I should be concerned about? I can confirm that private channels are working properly in general.
On the frontend the code is as follows:
let options = PusherClientOptions(
authMethod: AuthMethod.authRequestBuilder(authRequestBuilder: AuthRequestBuilder()
)
pusher = Pusher(key: pusherKey!, options: options)
class AuthRequestBuilder: AuthRequestBuilderProtocol {
func requestFor(socketID: String, channel: PusherChannel) -> NSMutableURLRequest? {
let request = NSMutableURLRequest(url: URL(string: "https://\(baseURLPrefix).xxxxxx.com/xxxxx/xxxxx")!)
request.httpMethod = "POST"
request.httpBody = "socket_id=\(socketID)&channel_name=\(channel.name)".data(using: String.Encoding.utf8)
request.addValue(
"Bearer " + authToken, forHTTPHeaderField: "Authorization"
)
return request
}
}
On the backend(Laravel application):
// Controller
public function presence_auth(Request $request)
{
$pusher = new Pusher(
config('broadcasting.connections.pusher.key'),
config('broadcasting.connections.pusher.secret'),
config('broadcasting.connections.pusher.app_id')
);
return $pusher->presence_auth($request->input('channel_name'), $request->input('socket_id'), AuthUser()->id);
}
Would this error occur if they had passed up a bad Bearer token to our backend?
You're using $pusher->presence_auth to create a signature for a private channel, i.e. a channel prefixed with private-. But presence_auth is intended to authenticate presence channels, i.e. channels prefixed with presence-.
If you wish to use presence data, you can use a presence- channel prefix. If you wish to use a private- channel without presence information, you can just use:
$pusher->socket_auth($request->input('channel_name'), $request->input('socket_id'))

How to get a MS Translator access token from Swift 3?

I am trying to work with a MS Translator API from Swift 3 (right now playing in playgrounds, but the target platform is iOS). However, I got stuck when I was trying to get an access token for OAuth2. I have following code (I tried to port the code from example at Obtaining an access token):
let clientId = "id".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let clientSecret = "secret".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let scope = "http://api.microsofttranslator.com".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let translatorAccessURI = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"
let requestDetails = "grant_type=client_credentials&client_id=\(clientId)&client_secret=\(clientSecret)&scope=\(scope)"
let postData = requestDetails.data(using: .ascii)!
let postLength = postData.count
var request = URLRequest(url: URL(string: translatorAccessURI)!)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("\(postLength)", forHTTPHeaderField: "Content-Length")
request.httpBody = postData
URLSession.shared.dataTask(with: webRequest) { (returnedData, response, error) in
let data = String(data: returnedData!, encoding: .ascii)
print(data)
print("**************")
print(response)
print("**************")
print(error)
}.resume()
Of course, I used a valid clientId and a valid clientSecret.
Now the callback prints following information. First, the returnedData contain a message that the request was invalid, along with a following message:
"ACS90004: The request is not properly formatted."
Second, the response comes with a 400 code (which fits the fact that the request is not properly formatted).
Third, the error is nil.
Now I was testing the call using Postman, and when I used the same URI, and put the requestDetails string as a raw body message (I added the Content-Type header manually), I got the same response. However, when I changed the body type in Postman UI to application/x-www-form-urlencoded and typed in the request details as key value pairs through its UI, the call succeeded. Now it seems that I am doing something wrong with the message formatting, or maybe even something bad with the Swift URLRequest/URLSession API, however, I cannot get a hold on to what. Can somebody help me out, please? Thanks.
OK, so after some more desperate googling and experimenting I have found my error. For the future generations:
The problem resided in encoding the parameters in the body of the PUT http request. Instead of:
let scope = "http://api.microsofttranslator.com"
.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
I have to use the following:
let scope = "http://api.microsofttranslator.com"
.addingPercentEncoding(withAllowedCharacters:
CharacterSet(charactersIn: ";/?:#&=$+{}<>,").inverted)!
Seems that the API (or the HTTP protocol, I am not an expert in this) have problems with / and : characters in the request body. I have to give credit to Studiosus' answer on Polyglot issue report.

Resources