LinkedIn login with Stormpath - swift

I am able to get the access token of my LinkedIn account in swift using the LinkedIn SDK. How do I authenticate this login with Stormpath?
[Update]
let APIURL = "https://api.stormpath.com/v1/applications/LI_APPLICATION_ID/accounts"
func sendRequestWithJSON(accessToken:String)
{
let json = [ "providerData" : ["providerId": "linkedin", "accessToken": accessToken] ]
do {
let jsonData = try NSJSONSerialization.dataWithJSONObject(json, options: .PrettyPrinted)
let username = STORMPATH_API_KEY_ID
let password = STORMPATH_API_KEY_SECRET
let loginString = NSString(format: "%#:%#", username, password)
let loginData: NSData = loginString.dataUsingEncoding(NSUTF8StringEncoding)!
let base64LoginString = loginData.base64EncodedDataWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
// create post request
let url = NSURL(string: APIURL)!
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
// insert json data to the request
request.setValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.HTTPBody = jsonData
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){ data, response, error in
if error != nil{
print("Error -> \(error)")
return
}
do {
let result = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String:AnyObject]
print("Result -> \(result)")
} catch {
print("Error -> \(error)")
}
}
task.resume()
//return task
} catch {
print(error)
}
}
I am passing accessToken fetch from linkedin to above function, but it return below result:
["message": Authentication required., "status": 401, "code": 401, "developerMessage": Authentication with a valid API Key is required., "moreInfo": http://www.stormpath.com/docs/quickstart/connect]
What's wrong I am doing?

LinkedIn is an interesting beast, since their mobile SDKs have two flaws:
An end user NEEDS the LinkedIn app to be installed, otherwise the "login" button will redirect the user to the App Store.
The mobile access token cannot be used on the server. See this screenshot from LinkedIn's iOS documentation
So - to get auth working on mobile, I would recommend using a server to handle the flow, so you don't have to worry about those two downsides. This is roughly:
The app will redirect the user to your webserver.
The webserver begins the LinkedIn authentication flow, and redirects the user to LinkedIn.
The user logs into LinkedIn, and gets redirected back to your webserver.
The webserver reads the response, and exchanges the Authorization Code with LinkedIn for an access token.
The webserver redirects your user back to the app, using a custom url scheme to send it the LinkedIn access token.
The app uses the LinkedIn access token to login to Stormpath.
Sound complicated? It's actually more straightforward than it seems. I actually wrote some demo code for this flow using Express.js & Swift if you want to try it out. Let me know if it works for you!

Related

Authenticating to a GCP HTTP Cloud Function with a Firebase ID Token doesn't work?

I know this is a duplicate question, but I haven't seen anyone present a swift and python implementation. In addition, i've tried everything listed in the other questions, and nothing seems to work
Environments and Conditions:
Calling the http Cloud Function from an iOS app after I get the ID
token (with the ID token)
Written in Swift
The Cloud Function is written in Python
I cannot use HTTP callables as they are not deployable via the current terraform infrastructure in place (at least not that I know of, but would be open to any ideas)
Problem:
So, I was under the assumption that including the Firebase ID token of a Firebase user inside of the Authorization header works ,but it hasn't been for me even with a force refresh. I get a 403 status response with message: The access token could not be verified. That being said, if I go into CLI and get the id token of my actual gcp user account via: gcloud auth print-identity-token then replace the header with said token, I am verified.
Swift Request Code (excuse the lack of convention, this is just POCing before I make a real implementation):
guard let user = Auth.auth(app: authAppToUse).currentUser else { fatalError("SearchMySQL -> user: \(String(describing: Auth.auth(app: authAppToUse).currentUser))") }
user.getIDTokenForcingRefresh(true) { (idToken, err) in
if err != nil{
fatalError("SearchMySQL -> user.getIDToken -> err: \(String(describing: err))")
}
guard let guardedIdToken = idToken else { fatalError("SearchMySQL -> guardedIdToken: \(String(describing: idToken))") }
let rawJSON = [
"data" : [
"table": table,
"search_by": searchBy,
"search_by_value": searchByValue
]
]
guard let guardedJSON = try? JSONSerialization.data(withJSONObject: rawJSON, options: .prettyPrinted) else {
return
}
guard let url = URL(string: "https://us-east1-fresh-customer-dev-ocean.cloudfunctions.net/mysql_search") else { return }
var request = URLRequest(url: url)
request.addValue("Bearer \(idToken)", forHTTPHeaderField: "Authorization")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = guardedJSON
let task = URLSession.shared.dataTask(with: request) { (data, response, err) in
if err != nil {
print(err?.localizedDescription)
fatalError("SearchMySQL -> URLSession.shared.dataTask -> err: \(String(describing: err))")
}
guard let guardedData = data else {
return
}
print(idToken)
print(response.debugDescription)
let json = try? JSONSerialization.jsonObject(with: guardedData, options: .allowFragments)
print(json)
completion(data)
}
task.resume()
}
Python Cloud Function:
def main(request):
"""Background Http Triggered Cloud Function for ********.
Validates authentication with Firebase ID token
Returns:
********.main(payload): http response code and data payload (or message)
"""
# validate request authorization
if not request.headers.get('Authorization'):
log.fatal('No Authorization Token provided')
return {'message': 'No Authorization Token provided'}, 400
try:
id_token = request.headers.get('Authorization')
auth.verify_id_token(id_token)
except (ValueError, InvalidIdTokenError, ExpiredIdTokenError, RevokedIdTokenError, CertificateFetchError) as e:
log.fatal(f'Authorization `id_token` error: {e}')
return {'message':f'Authorization `id_token` error: {e}'}, 400
data_payload = request.get_json(force=True)
if not data_payload.table:
log.fatal('Payload missing table field.')
return {'message': 'Payload missing table field'}, 422
if not data_payload.search_by:
log.fatal('Payload missing search_by field.')
return {'message': 'Payload missing search_by field'}, 422
return ********.main(data_payload)
Ideas/Questions:
Aren't Firebase ID tokens equivalent to Google ID Tokens?
Could there be an iAM permission issue for the (auto generated)
firebase service account?
Do I need to also add any of the plist values to the idtoken when
sending it over in the header?
Could it be something with the rules of my cloud function?
Am I missing something, or is this intended/expected behavior? With
http callables being regular http functions but with protocols
packaged to facilitate, I would think that this is a relatively easy
implementation....
I've thought of the route of using an admin function to send a
message to the mobile instance that needs a google id token during
login, but the overhead and latency would result in issues.

Attempts to access rapidAPI yahoo finance give me error 403 through swift

I am trying to use rapidAPI to access the Yahoo Finance API for a personal project. I registered for a free account and got an API key. Rapid API allows their users to copy and paste code to request data, so I took their demo code for accessing the API. Here it is for swift when trying to access details about a stock:
import Foundation
let headers = [
"x-rapidapi-host": "apidojo-yahoo-finance-v1.p.rapidapi.com",
"x-rapidapi-key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" //private key
]
let request = NSMutableURLRequest(url: NSURL(string: "https://apidojo-yahoo-finance-
v1.p.rapidapi.com/stock/get-detail?region=US&lang=en&symbol=APPL")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
When I run this code, it gives me error 403. This means that this data is forbidden according to their website. I have tried using other APIs and copying and pasting the demo code and they work fine. This API shouldn't be depreciated because it is running on the website demo. Here is the returning message I was given in Xcode:
I just checked Yahoo Finance API. The GET /get-detail endpoint seems to be deprecated now. Despite the fact that it's deprecated, it's still working for me.
Make sure to subscribe to this API. this could be the cause of the 403 code

JWT Request Made but It tells that request does not contain access token

I tried to make a Request with JWT Authorization, The server is Using Python/Flask-Restful. The API Works on Postman, so I guess there must be something wrong with my IOS Code. The server returns an error shows that
"Authorization Required. Request does not contain an access token",
I`m making the request from IOS Using following code.
func GetUserData(username: String, accesstoken: String,completion: #escaping (_ result: UserDataModel) -> Void){
let url = URL(string: "http://********/****/\(****)")
var request = URLRequest(url: url!)
request.httpMethod = "GET"
request.addValue("Authorization", forHTTPHeaderField: accesstoken)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response as? HTTPURLResponse{
if response.statusCode != 200 {
print("Server Error When Update User Data")
} else {
if let data = data {
do {
******
completion(Data)
}
catch {
print(error)
}
}
}
}
}.resume()
}
I have no idea What is going on, Any help?
It looks like you're adding the header:
Bearer base64junk: Authorization
When instead you want:
Authorization: Bearer base64junk
You just have the parameters to addValue(_:forHTTPHeaderField:) backwards. You want this instead:
request.addValue(accesstoken, forHTTPHeaderField: "Authorization")
This should be obvious if you read that line of code like an English sentence ("value authorization for header field access token"?). In the future, you could also use something like Charles Web proxy to intercept your requests and verify that they are indeed formed the way you expect.

Send Firebase Swift Push using NSURLSession HTTP Post

I set up my app so that users when they login subscribe to a topic with their user UID. Whenever a user sends a message to another user I will be calling the function below that I am hoping to trigger the push.
func sendPushNotification(toUser: String, message: String) {
let urlString = "https://fcm.googleapis.com/fcm/send"
let topic = "\topic\\(toUser)"
let url = NSURL(string: urlString)!
let paramString = "to=\(topic)"
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.HTTPBody = paramString.dataUsingEncoding(NSUTF8StringEncoding)!
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) in
do {
if let jsonData = data {
if let jsonDataDict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.AllowFragments) as? [String: AnyObject] {
NSLog("Received data:\n\(jsonDataDict))")
}
}
} catch let err as NSError {
print(err.debugDescription)
}
}
task.resume()
}
Based on Firebase docs I am supposed to do this HTTP request:
Send to a single topic:
https://fcm.googleapis.com/fcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA
{
"to": "/topics/foo-bar",
"data": {
"message": "This is a Firebase Cloud Messaging Topic Message!",
}
}
I am struggling to see what I pass in the paramString for the message to be sent based off of their example. Also, where do I define the Authorization:key=
Sending a message with Firebase Cloud Messaging requires that you provide your server key in the Authorization header. As the name suggests, this key should only be used in code that runs on an app server (or other infrastructure that you control). Putting the server key into the app that you ship to your users, means malicious users can use it to send messages on your behalf.
From the Firebase documentation on sending messages to topics (emphasis mine):
From the server side, sending messages to a Firebase Cloud Messaging topic is very similar to sending messages to an individual device or to a user group. The app server sets the to key with a value like /topics/yourTopic.

Setting Headers for OAuth Authenticated User (Bearer)

I am experimenting on OAuth2 (on Laravel) & Swift with Alamofire. I successfully got token, however I couldn't set the headers to be able to get authenticate-specific data from Api.
Without Alamofire, I was setting this header and it was working.
func me(handler: (data: NSDictionary?, error: String?) -> Void)
{
let url = NSURL(string: "/me", relativeToURL: self.baseUrl)
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "GET"
if let t = self.getAccessToken()
{
request.setValue("Bearer \(t)", forHTTPHeaderField: "Authorization")
}
}
However I didn't get what to do with Alamofire.
Now I can use getAccessToken() to get token as a string, but I got really confused adapting it to Alamofire style.
I tried this but doesn't pass the authentication middleware for a reason:
let token = getAccessToken()
print(token!) // prints
let headers = ["Authorization":"Bearer \(token)"]
Alamofire.request(.GET, userDetailsEndpoint!, headers: headers)...
Still getting error The resource owner or authorization server denied the request