Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to get token without clicking link #12

Closed
e-oz opened this issue Nov 4, 2014 · 42 comments
Closed

How to get token without clicking link #12

e-oz opened this issue Nov 4, 2014 · 42 comments

Comments

@e-oz
Copy link

e-oz commented Nov 4, 2014

Hi.
I need to access api but without any user involved to this process. It's absolutely not acceptable to force users to click some external links, so I need the way to generate token automatically.
Is it possible?

@mfairch
Copy link
Contributor

mfairch commented Nov 5, 2014

Can you explain your use case as to why you need this?

@e-oz
Copy link
Author

e-oz commented Nov 5, 2014

To add/remove contacts. If we create user in app, we want just add them into contacts list.

@merlindiavova
Copy link

any update on this?

@kressaty
Copy link
Contributor

I'm not sure I follow the use case here - you can always just automatically redirect a user through the OAuth flow. If you want to generate your own token for your own use in an application that doesn't require individual user usage, you can always generate one and save it with a tool like the Runscope Oauth Token Generator

@bensan
Copy link

bensan commented Feb 7, 2015

@kressaty Can you elaborate on your answer above? My InfusionSoft app integration is completely transparent to the user and I will not be able to pop up a browser for them.

@mfairch
Copy link
Contributor

mfairch commented Feb 10, 2015

@bensan Can you elaborate on what your integration is/does? I can elaborate on @kressaty answer however need more context to give you the best answer possible.

@e-oz
Copy link
Author

e-oz commented Feb 10, 2015

Not sure if authors are trolling us or really don't understand what does mean "without any user involved to this process".

@merlindiavova
Copy link

@Jamm If i understand what you are asking correctly then my answer may help. Within your app create a login process for Infusionsoft and store the token data to your database or file. Now for every call you make refresh the token and save it again. Infusionsoft allows indefinite refresh tokens so the so as long as you refresh the token before every call your app will function without the need of "user interaction" indefinitely

@mfairch
Copy link
Contributor

mfairch commented Feb 10, 2015

@merlindiavova The is the correct process. @Jamm wants to have no user interaction at all but that will never happen because the user has to provide some sort of information for the integration to work, items such as their api key or click an authorize button.

@e-oz
Copy link
Author

e-oz commented Feb 10, 2015

@merlindiavova thanks for workaround, although Infusionsoft should be ashamed of this workaround, because it shows their API is stuck somewhere in middle ages.
@MicFai user can give this information to application not only through Infusionsoft API, there are billions ways to get user's info. And any sane app will show own UI to get user's information, not some third-party library.

@silasrm
Copy link

silasrm commented Mar 8, 2015

I've the same problem. Don't exist example code with refreshToken using and this documentation is very very very basic.

After endOfLife of the Token, the refreshToken return Error 400 Bad Request. If endOfLife of the Token is valid, refreshToken added 2 seconds in this value.

How to solve this problem?

@kressaty
Copy link
Contributor

kressaty commented Mar 9, 2015

It's really important that you understand how OAuth works. There is no possible way for a user to have "no interaction" and provide you the information to access their data. The only difference here is instead of a user giving you their API key and saving it in an input field, you're asking them to click a button - a much easier task, as finding an API key is never easy.

@silasrm Make sure to refer to this document: https://developer.infusionsoft.com/docs/read/Getting_Started_With_OAuth2

And importantly, note that when you request a new refresh token, you are also given a new access token, that you'll need to make the next subsequent refresh request.

@e-oz
Copy link
Author

e-oz commented Mar 9, 2015

Looks like you don't understand what we want. We want to send requests to your API without showing to user third-party interfaces. It's critically important for some apps. You providing explanations why it doesn't work, when we ask to make it possible (change something), not explain us why it's not possible in current implementation of your API. So I don't have any hope you will change it.

@e-oz e-oz closed this as completed Mar 9, 2015
@kressaty
Copy link
Contributor

kressaty commented Mar 9, 2015

@Jamm how did you do this before? At some point, did you not ask your users for their API key?

@silasrm
Copy link

silasrm commented Mar 9, 2015

I authorize my app make "connected" to Infusion using Oauth, for this I'm saving access token object in database.

But when I make a request after endOfLife, your API return 400 Bad Request. If I make a refresh token before the request, but after endOfLife, return 400 Bad Request. endOfLife is very very short.

My app flow:
Authorize and save token:

  1. MyApp -> Infusion Oauth Authorize window -> Authorized! -> redirect to MyApp with code data -> save in database;

When a client buy my product, he is added in Infusion:
2. MyApp -> load access_token data from database -> call Infusion API -> return treatment.

In step 2, user interaction is not required.

@ArifLateefShah
Copy link

I am trying to add a contact in infusion from my website and for that, i am using API.In that api, its asking for token and i don't have any idea where from I can get that token.
I have tried this line but its generating error for "code".

Can anyone help ???
$infusionsoft->requestAccessToken($_GET['code']);

@jakerator
Copy link

jakerator commented Mar 22, 2017

I have similar problem. I need to develop billing system that should work in background and do some API calls every day. It works on server. Without any front-end. So user is not involved there anyhow. There is nobody who can click that request tocken link when needed.
Old IS API allowed long-live key-pair AUTH, which allowed all this.
How to do the same now, with oAuth API? I can't always have somebody who will refresh tockens. Our software should work 24h/day automatically. And there is no place for any situations when token is expired and can not be used anymore (e.g. our back-end app was down more then 1 hrs and wasn't able to refresh token). What to do in such cases? How to request a new token withuot "human"?
Any ideas?

@bgpartridge
Copy link

@ArifLateefShah please repost in http://community.infusionsoft.com/c/api. That is forum for asking for help once you've expended your understanding of the documentation: https://developer.infusionsoft.com/docs/rest/#authentication

@bgpartridge
Copy link

@jakerator At some point in time the user must authorize access to their app (this was true with the legacy API key as it is with OAuth - though the steps the user took were actually more cumbersome with legacy). Once the user has authorized you to access their data on their behalf then you receive an access token as well as a refresh token. You can use that refresh token to get a new access token so that it effectively never expires: https://developer.infusionsoft.com/docs/rest/#authentication-refresh-an-access-token. We are also working on a mechanism that allows the user to generate a token (just like legacy API key) instead of requiring the app to have them log in. This would still require automating the refresh of the access token in the background, as that is standard OAuth authorization code spec.

@jakerator
Copy link

@bgpartridge is there any expiration of that "refresh_token"?

If I undersand you correctly, only human may request new "access_token" without valid "refresh_token"?
I'm trying to evaluate a situation when our server is down, or DB data loss. Do we need always have a human who will request new access token when we lost previous one? If so, that is absolutelly not suitable for us. We need a solid solution, that will work all the time withuot any human work and be always stable.

@mfairch
Copy link
Contributor

mfairch commented Mar 22, 2017

@jakerator Refresh tokens have a long lived lifetime. I've personally tested a refresh token being a year old still working.

@bgpartridge
Copy link

@jakerator our refresh tokens have no time based expiration. They only expire when a new access token is given (a new refresh token will be given at the same time, rendering the previous refresh token invalid). If there was a catastrophic failure - you lose the refresh token for example - then yes you have effectively (and appropriately for security purposes) lost authorization to access the users data and the user needs to re-authorize you.

@jakerator
Copy link

@bgpartridge what if I write a web-scrape bot-script, which will open "request token" url, simulate button click and browser session instead of "Human". Will this be legal?

@bgpartridge
Copy link

@jakerator Are you authorizing against your own IS application or is this something you'll use to access other customer's data? If it's the latter (which is the case I was assuming) then the user has to log in - no way around it. If this is for your own IS app, then until we have the other mechanism to handle the initial token grant complete, you'll need a way to log in yourself to obtain the initial access token. This would also be convenient should anything go awry, so you can quickly generate a new token.

@jakerator
Copy link

jakerator commented Mar 22, 2017

@bgpartridge This will work only with our own IS Application. We need to make it absolutelly automated. To work withuot any human actions in any cases.

@bgpartridge
Copy link

@jakerator I'm sorry, but at this time the initial obtaining of the token can only be done with a user (yourself in this case) typing in credentials. After than it can be completely automated. We are aware of the need to handle cases beyond this more easily and are working to that end.

@hi2u
Copy link

hi2u commented Nov 21, 2017

Seeing the Infusionsoft devs seem to be confused about the use case, let me explain:

We have a PUBLIC website, which has some backend PHP code with interacts with the Infusionsoft API. We might also have cron jobs that execute command line scripts to do stuff on the API... in which case there is no "user" at all.

Visitors to our PUBLIC website are random people browsing the web, they've never even heard of infusionsoft.

For this common use case of an API (directly from our server to the API), it's crazy making us write and maintain a bunch of oauth code dealing with multiple tokens, redirections and all this kind of stuff. Especially having to polute our PUBLIC website's code with sysadmin tasks that shouldn't be needed to begin with.

Just give us an API key that we can paste into our server-side config files. This is how every other server-side API I've ever used operates.

There should be zero need for for us to write user-facing client-side code in our project for this. The whole thing should be able to be used from CLI scripts.

@jakerator
Copy link

I'm completelly agree with @hi2u . He explained the issue most accuratelly.
And it seams there is still no solution for that.
We've already migrated to Stripe because of that.

@brumiser1550
Copy link

@jakerator and @hi2u I think you all are thinking too hard about this. I have built many "public" apps using OAuth. The only time you should ever have to authorize the app is one time if you are maintaining your tokens appropriately.

When you first auth your app (you, as in the developer for the app) you save the token to a text file, json file, database, whatever you want, just make it secure. Then when you go to make any API calls anywhere you load the token and check if it is expired and if so refresh it and save back to your location.

The users never see or know anything about IFS just what they see on your site. If your API is being hit more than once a day and refreshing tokens you have nothing to worry about. If you run less often and could go outside the time limit of the access token then you should setup a script on a cron job to renew your token. There is quite literally almost 0 interaction.

Here is a sample of what I have done before (most bare code I could find) https://gist.github.com/brumiser1550/39d5151bcb92acfff13b3b147b325b5a

I do not want to share my storage class for personal reasons but hopefully you can make your own.

@jakerator
Copy link

@brumiser1550 e.g. stored api key was lost or damaged (e.g. bbackup restored). And lets imagine you have a very high-loaded website, with thouthands of different daily operations that depends on that InfusionSoft integration. All that becomes DEAD until some "human" re-activate your IS key again.
In my case this is not a solution. My services MUST be fully automated and to not depend on any human watch.

@brumiser1550
Copy link

@jakerator If your very high-load website needs a back up and you are already doing human interaction and testing everything what is one more step to make sure you have a valid token again?

If you are that worried you can build in redundancy. Store your token onsite and in a backup off site. If/when the local token is destroyed because of a backup or something pull from the offsite storage.

I understand wanting the ability to never need to have to touch it but it just does not work that way. If I have a Google integration(which I do) that reads my drive files I have to be OAuthed and grant permissions for that and store the token. If the server goes down or I lost that token I have to reauth again.

There are a plethora of integrations that work this way, and yes, there are some that do not, but most integrations that utilize oath tokens are going to act the same way.

@Xavious
Copy link

Xavious commented Jan 11, 2018

Like everyone else, I need to automate synchronization from another system to Infusionsoft, without any user interaction. Unfortunately the documentation is extremely vague for this use case and doesn't provide any example code. I've been digging into the raw SDK code itself, but it's burning up a lot of my time as I try to unravel the authentication code, cleanly written as it is.

So what is the required format for a token when using the setToken() method of the Infusionsoft SDK?

I've granted access to my application through the Infusionsoft dashboard through an interface that asks for the client_id and client_secret. It then provides an access_token and a refresh_token: https://imgur.com/a/Cj1NE

I've tried setting the token with a raw string like so:

$infusionsoft = new \Infusionsoft\Infusionsoft(array(
	'clientId'     => $config->clientId,
	'clientSecret' => $config->clientSecret,
	'redirectUri'  => 'http://example.com/dev.php'
));
$token = 'jguvs88uwub4wwas654z5g8v'
$infusionsoft->setToken($token);

I've also tried adding it as part of an array like I saw in the Token class constructor (NOTE: The dashboard does not provide expiration dates, just token strings):

$accessToken = 'jguvs88uwub4wwas654z5g8v'; 
$refreshToken = 'ws7k0kyvz7jgmnubecss2grb';
$token = array(
	'access_token' => $token,
	'resfresh_token' => $refreshToken
);
$infusionsoft->setToken($token);

After setting the tokens I run a simple test using the example from the API docs:
$tasks = $infusionsoft->tasks()->all();

Which presents nothing, but an unsatisfactory error about an expired token:

PHP Fatal error: Uncaught exception 'Infusionsoft\TokenExpiredException'

@brumiser1550 Your example is great, except for the fact that the token structure is abstracted by your storage class, and getting the SDK to accept the token in the first place is the problem I seem to be having.

NOTE: I have regenerated these tokens multiple times. It should NOT be expired.

(I'm posting this for posterity, as I seem to have unraveled part of my problem before posting. I'll add it afterwards, but I feel like this is probably a problem many are having.)

@Xavious
Copy link

Xavious commented Jan 11, 2018

So I had two problems that were triggering the exception:

  1. expires_in is a required field of the Token array. The dashboard says it is good for 24 hours, so just generate the time for 23 hours to be on the safe side. If this is missing from the array, it will throw an expired token exception
  2. Using the setToken() method will fail if you pass it just an array. It actually requires a Token OBJECT. This will trigger the expired token exception as well. Here is an example of the setToken() method being used by another method in the Infusionsoft class:
    public function requestAccessToken($code)
    {
        $params = array(
            'client_id'     => $this->clientId,
            'client_secret' => $this->clientSecret,
            'code'          => $code,
            'grant_type'    => 'authorization_code',
            'redirect_uri'  => $this->redirectUri,
        );

        $client = $this->getHttpClient();

        $tokenInfo = $client->request('POST', $this->tokenUri, [
            'body'    => http_build_query($params),
            'headers' => ['Content-Type' => 'application/x-www-form-urlencoded']
        ]);

        $this->setToken(new Token(json_decode($tokenInfo, true)));

        return $this->getToken();
    }

Notice this line specifically:

$this->setToken(new Token(json_decode($tokenInfo, true)));

Honestly to hack around this for my tests, I changed the setToken method in the Infusionsoft class from:

    /**
     * @param Token $token
     */
    public function setToken($token)
    {
        $this->token = $token;
    }

To this:

    /**
     * @param Token $token
     */
    public function setToken($token)
    {
        $this->token = new Token($token);
    }

Note: If you actually use this technique and change the core SDK, you'll need to change all references within the class using setToken() to not pass the object in, since it's being instantiated within the setToken() method itself now. Also NOTE, if you update this SDK with composer, you're going to break it later. So it's probably a better idea to create your own Token storage class like @brumiser1550 did and instantiate the object there.

I'm sure there is a reason they didn't just implement the Token object like this, but I don't know why. I'm merely using these examples to illustrate to anyone suffering oauth issues in the Infusionsoft SDK.

So now I have to work through the refresh token process, but presumably if you're saving your tokens and tracking the "expires_in" attribute, then you should be able to use your refresh token to get new tokens indefinitely. Anyway, that's my next challenge. Hopefully this helps others who are ready to set this SDK on 🔥 :D

@brumiser1550
Copy link

brumiser1550 commented Jan 11, 2018

Hey @Xavious,

I read through your issues want to address some things.

First, the reason the setToken method is that way because I believe most people store a serialized version of the token object, so when unserialized it can be passed back as a straight token object and work.

You can just do this with your token array

$accessToken = 'TOKENREMOVED';
$refreshToken = 'TOKENREMOVED';
$token_params = array(
    'access_token' => $token,
    'resfresh_token' => $refreshToken
);
$token = new Token($token_params);
$infusionsoft->setToken($token);


if ($infusionsoft->isTokenExpired()) { 
    $infusionsoft->refreshAccessToken();
}
//Save your new token to storage here as a serialized object. It will contain all pieces of information.

If you need more help maybe we can just set up a quick chat and help you work through it.

@Xavious
Copy link

Xavious commented Jan 11, 2018

Thanks for the response @brumiser1550

I tried instantiating it like that previously, but I had a namespace issue or something. I get a little mixed up with how to properly reference inside libraries sometimes.

I was able to instantiate the Token by using more specificity, similar to instantiating the Infusionsoft object itself like so:

$token = new \Infusionsoft\Token(array(
	'access_token' => $accessToken,
	'resfresh_token' => $refreshToken,
	'expires_in' => 1 
        // ^ Note this value is just for testing and to get the token through the expire check
));

@brumiser1550
Copy link

brumiser1550 commented Jan 11, 2018

So did you get everything else working?

If you want to say
new Token()
you need to add a
use Infusionsoft\Token;
to the top of your php file.

@Xavious
Copy link

Xavious commented Jan 11, 2018

I did get the token working yes.

I'm going to be working on the refresh aspect now, to make sure I can maintain access to the API indefinitely. Most of what I posted previously was for posterity and to hopefully help other people who are struggling. The SDK example only shows how to set up a token through the SDK when a user authorizes the app, so it isn't immediately clear how to properly create a data structure to manually create your own token.

The only tutorial for this sort of use case is here: https://developer.infusionsoft.com/tutorials/making-oauth-requests-without-user-authorization/ and it leaves much to be desired. Someone should take some time to rewrite it with a few simple examples. It'd probably clear up a lot of confusion.

At any rate, I appreciate your response. I'm going to start setting up token refreshes now.

@brumiser1550
Copy link

Awesome. You can always refresh and get a new set of tokens without them being expired. The old tokens will continue to work until they expire. A good way to test is to artificially change the expires_at so you trigger your expired handler.

I like to check before my scripts start processing to make sure that my token does not invalidate during the process.

    if ($infusionsoft->getToken()->endOfLife - time() < 7200) {
        $token = $infusionsoft->refreshAccessToken();
    }

@Xavious
Copy link

Xavious commented Jan 11, 2018

@brumiser1550 Out of curiosity, why wouldn't you just use use the built in Token method isExpired() ?

@brumiser1550
Copy link

Because I do not want my token to expire in the middle of a long running script and throw an exception. So on long running scripts I check if it is going to expire within a time limit and refresh if it is going to expire soon and refresh it if so.

@skylord123
Copy link
Contributor

skylord123 commented Nov 21, 2019

After dealing with the Infusionsoft API further I can say that I hate everything about it. This is a 5 year old issue that they said they would improve yet we have no progress what-so-ever.

  • Their API is fractured into XML and REST and they still haven't added full functionality to the later (despite it being a long time).
  • The oauth API implementation is the worst I have ever used (server-to-server communication shouldn't be this annoying and badly designed).
  • There are several issues that many people have requested yet Infusionsoft just ignores their customers (for example: https://community.infusionsoft.com/t/add-api-write-access-for-dataformtab/72979 )

As a CRM that stores a bunch of important information for a business being able to communicate with other systems should absolutely be number 1 priority. The API should be able to do everything you can do from the UI (and I would argue it should be able to do even more).

If anyone is reading this and API support is key don't go with Infusionsoft. You will be very disappointed, especially with their support.

I really hope they open their eyes and see the value of offering a better more feature complete API.

@congthang1
Copy link

Took me a day to figure out how it can be terrible this way until reading @skylord123 comment. They make the life harder, wonder how many people using this. Just give us button to Generate api key like any other do then 100 topics like this will gone!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests