How to send Dialogflow an access token from Swift using ApiAi Pod - ios

While following this tutorial: https://medium.com/#pallavtrivedi03/integrating-dialogflow-as-a-chat-bot-in-an-ios-app-e66a4c7f2723
I was unable to find out how to interact with dialogflow intents that require authentication via the swift app. Once the dialogflow path reaches a point where it needs authentication, the request is not met with a response.
I have successfully sent requests and received responses from dialogflow using the following:
func performQuery(senderId:String,name:String,text:String)
{
let request = ApiAI.shared().textRequest()
if text != "" {
request?.query = text
} else {
return
}
//print("request: ")
request?.setMappedCompletionBlockSuccess({ (request, response) in
let response = response as! AIResponse
print(response.result.parameters)
//print(response.result.action)
I am not an experienced swift user so it could be that I am missing something simple, however, I am unable to find any documentation on the topic.
Any information on ways to interact with dialogflow using oauth2 would be greatly appreciated.

Related

AWS Amplify iOS SDK : FederatedSignIn Failed to retrieve authorization token on Amplify.API.post

I've been working with the Amplify SDK to get federatedSignIn working with my iOS app with "Sign in with Apple" and Cognito to eventually make calls to API Gateway / Lambda functions.
TL;DR : My access token does not appear to be "automatically included in outbound requests" to my API as per the last paragraph of this section of the docs : Cognito User pool authorization
I have successfully authenticated using the tutorial found here Authentication Getting Started and other various Youtube videos on the Amazon Web Services channel.
Upon successful sign in through Apple I'm given an ASAuthorizationAppleIDCredential object. This contains the user field (token) which I pass to the Amplify.Auth class using the following Swift code :
func signIn (with userId: String)
{
guard
let plugin = try? Amplify.Auth.getPlugin(for: AWSCognitoAuthPlugin().key),
let authPlugin = plugin as? AWSCognitoAuthPlugin,
case .awsMobileClient (let client) = authPlugin.getEscapeHatch()
else
{
return
}
client.federatedSignIn(providerName: AuthProvider.signInWithApple.rawValue, token: userId) { (state, error) in
if let unwrappedError = error
{
print (unwrappedError)
}
else if let unwrappedState = state
{
print ("Successful federated sign in:", unwrappedState)
}
}
}
All appears to be successful and to double check I use the following bit of code to ensure I'm authorized :
func getCredentialsState (for userId:String)
{
let provider = ASAuthorizationAppleIDProvider()
provider.getCredentialState(forUserID: userId) { (credentialsState, error) in
if let unwrappedError = error
{
print (unwrappedError)
}
switch credentialsState
{
case .authorized:
print ("User Authorized")
case .notFound, .revoked:
print ("User Unauthenticated")
case .transferred:
print ("User Needs Transfer")
#unknown default:
print ("User Handle new use cases")
}
}
}
In the console I see "User Authorized" so everything appears to be working well.
However when I then go to make a call to Amplify.API.post I get the following error:
[Amplify] AWSMobileClient Event listener - signedOutFederatedTokensInvalid
Failed APIError: Failed to retrieve authorization token.
Caused by:
AuthError: Session expired could not fetch cognito tokens
Recovery suggestion: Invoke Auth.signIn to re-authenticate the user
My function for doing the POST is as follows :
func postTest ()
{
let message = #"{'message": "my Test"}"#
let request = RESTRequest (path: "/test", body: message.data(using: .utf8))
Amplify.API.post (request:request)
{
result in switch result
{
case .success(let data):
let str = String (decoding: data, as: UTF8.self)
print ("Success \(str)")
case .failure(let apiError):
print ("Failed", apiError)
}
}
}`
I then went into the API Gateway UI and changed the generated Method Request on my resource from AWS IAM to my Cognito User Pool Authorizer thinking this was the issue. I also changed the awsAPIPlugin authorizationType to "AMAZON_COGNITO_USER_POOLS" in my amplifyconfiguration.json file. This unfortunately did not have any affect.
I've seen posts such as this issue User is not created in Cognito User pool for users logging in with Google federated login #1937 where people discuss the problem of having to to use a web ui to bring up the social sign in. I understand that Apple will reject your app sometimes for this. Therefore this is not a solution.
I then found this post which seems to resolve the issue however this appears to use the old version of the SDK? Get JWT Token using federatedSignIn #1276
I'm not great with Swift (I'm still an Objective C expert, but am slowly learning Swift) so I'm uncertain which path to go here and whether this is actually a solution? It does seem to be quite more complicated than the function I have that does my POST? The RESTRequest does seem to be a simple and easy solution but I'm uncertain how to pass it the Authorization token (or even how to get the token if it is needed here).
However, everything I've read about the SDK is that the authorization should be handled automatically in the background according the docs in my first link above. Specifically pointed out, again, here : Cognito User pool authorization. The last paragraph here states 👍
With this configuration, your access token will automatically be included in outbound requests to your API, as an Authorization header.
Therefore, what am I missing here as this does not appear to automatically include my access token to my outbound requests to my API?

Integrate Google drive into my app: Value of type 'GIDAuthentication' has no member 'fetcherAuthorizer'

I try to integrate google drive into my app to list mp3, m4a music files. Now I have made Google sign working, means I could signIn and signOut google account.
But when I follow google's api doc to this step Make an API call with fresh tokens, I got above error on line let authorizer = authentication.fetcherAuthorizer(). This function fetcherAuthorizer is not found.
I think maybe this doc is not up to date with latest google sdk. So I did a few search on Google...then it appears there is really rare stuff about this topic. What I found is all used this same code authentication.fetcherAuthorizer().
Thus, guys. Is there any update about this code?
user.authentication.do { authentication, error in
guard error == nil else { return }
guard let authentication = authentication else { return }
// Get the access token to attach it to a REST or gRPC request.
let accessToken = authentication.accessToken
// Or, get an object that conforms to GTMFetcherAuthorizationProtocol for
// use with GTMAppAuth and the Google APIs client library.
let authorizer = authentication.fetcherAuthorizer()
}
Today, I find the answer in this thread googlesignin-ios
The reason is Swift cannot find the fetcherAuthorizer from Objective-C library, we must manually import it import GTMSessionFetcher.

Error on refreshing auth token in Firebase iOS

I'm trying to refresh my auth token in Firebase using swift as follows
let currentUser = Auth.auth().currentUser
currentUser?.getIDTokenForcingRefresh(true) { idToken, error in
if error != nil {
return;
}
self.userDataManager.fetchUser()
}
It's hitting the error return statement with,
message = "Requests to this API securetoken.googleapis.com method google.identity.securetoken.v1.SecureToken.GrantToken are blocked.";
status = "PERMISSION_DENIED";
I search around the docs, But couldn't find a proper solution for this. Please help with this.
I had the same error but with the Web APIs. Solved it by
Going to https://console.developers.google.com/apis/credentials,
Clicking on the API key
Enabling the "Token Service API", along with "Identity Toolkit API"

Using API-Key in Vapor

I'm developing a simple web API with Vapor. To give more context, I'm newbie in backend development.
The consumer of the API is going to be an iOS app. Currently, I don't need the users to sign up to use the app. And I would like to keep it like that.
On the other hand, I would like to have some authentication to avoid that anyone could use the API I'm developing.
Looking for information I've found how implement authentication. But the examples I've seen are based on creating users in the backend for each user of the app. What I don't want to do. I would like to use an api-key as we do normally when we use third-party api's.
How could I have "api-key authentication" with Vapor ??
Or, should I just create an unique user/password that it's shared by all the users of the iOS app (that use the API) and then use basic or token authentication?
Thank you very much!
Carlos
One way around this is to create a fake token and use either the TokenAuthenticationMiddleware or more likely a custom middleware that checks the incoming token.
However, be aware that there is nothing stopping anyone from inspecting the traffic coming from your app to view the token and then using that to access your API.
Following Tim idea and an example from the book Server Side with Vapor (by the Raywenderlich.com Tutorial Team) I've created this custom middleware that makes the work:
final class SecretMiddleware: Middleware {
let secret: String
init(secret: String) {
self.secret = secret
}
func respond(to request: Request, chainingTo next: Responder) throws -> Future<Response> {
guard let bearerAuthorization = request.http.headers.bearerAuthorization else {
throw Abort(.unauthorized, reason: "Missing token")
}
guard bearerAuthorization.token == secret else {
throw Abort(.unauthorized, reason: "Wrong token")
}
return try next.respond(to: request)
}
}
extension SecretMiddleware: ServiceType {
static func makeService(for worker: Container) throws -> SecretMiddleware {
let secret: String
switch worker.environment {
case .development:
secret = "foo"
default:
guard let envSecret = Environment.get("SECRET") else {
let reason = "No SECRET set on environment."
throw Abort(.internalServerError, reason: reason)
}
secret = envSecret
}
return SecretMiddleware(secret: secret)
}
}

How to access data field in Dialogflow?

As the Dialogflow documentations states, the data field represents
Additional data required for performing the action on the client side.
The data is sent to the client in the original form and is not
processed by Dialogflow.
How should one access it in the iOS framework?
request?.setMappedCompletionBlockSuccess({ (request, response) in
...
}
I couldn't find it in the response object and can't find any documentation for iOS.
Thanks.
Your question is a bit vague (can you edit and narrow it down?), but i think you got it the other way round, what that snippet of documentation that you pasted means is that you are supposed to send that payload to DialogFlow and it will forward it to a connected Client (e.g Messenger, Slack etc) un-touched. It simply means that DialogFlow assumes that you know what you are doing.
Here is a sample Fulfilment response to DialogFlow in JS
module.exports.sendGenericMessageWithText = function(message) {
return {
data: {
facebook: [
{
text: message
]
}
}
}

Resources