# Unable To Get Twitch API OAuth For PHP

**URL:** <https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113>\
**Category:** API\
**Created:** [February 28, 2022, 1:03am UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113 "2022-02-28T01:03:56Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![RonWhoCares](https://avatars.discourse-cdn.com/v4/letter/r/df788c/32.png) [@RonWhoCares](https://staging-discuss.dev.twitch.com/u/RonWhoCares)\
**Post date:** [February 28, 2022, 1:03am UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/1 "2022-02-28T01:03:56Z")

</div>

I have been unable to get an OAuth token. I have spent about 4 hours trying. The various iterations and changes the Twitch API has been through is leaving me unsure and confused. The Twitch Developers now have a message posted stating V5 API being decommission on February 28th 2022. I am lost.

This is where I am at right now. The “code” below is from the **Getting authorization code** [here](https://write.corbpie.com/twitch-api-authentication-with-oauth-using-php/).

```
<?php

    function file_get_contents_curl($url) {
        $curlHeader = [
            'client_id' => '"CLIENT ID"',
            'client_secret' => '"CLIENT SECRET"',
            'code' => '"POST TOKEN"',
            'grant_type' => '"AUTHENTICATION CODE"',
            'redirect_uri' => 'https://rons-home.net'
        ];

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_AUTOREFERER, TRUE);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $curlHeader);
        $data = curl_exec($ch);

        curl_close($ch);
        return $data;
    }

print_r( file_get_contents_curl( 'https://id.twitch.tv/oauth2/authorize' ) );

```

Response

```
{"status":400,"message":"missing response type"}

```

Any help gratefully received and appreciated.

---

<div class="post-metadata">

**Author:** ![BarryCarlyon](https://d1d6mrmdu7ztse.cloudfront.net/user_avatar/staging-discuss.dev.twitch.com/barrycarlyon/32/253_2.png) [@BarryCarlyon](https://staging-discuss.dev.twitch.com/u/BarryCarlyon)\
**Post date:** [February 28, 2022, 12:49pm UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/2 "2022-02-28T12:49:33Z")

</div>

`grant_type` shouldn’t be `"AUTHENTICATION CODE"` for start.

The linked example isn’t very good. It just goes “well heres how you pass stuff to some function that someone else wrote” nor covers the token types and likely usage, and railroads you into getting a user token.

I’m not sure if you are trying to do `User tokens` or an `App Access Token`.

This kinda looks like user token step three since you have a `code` but the data should not be passed as `headers`, they should be `post fields`.

This is a complete example of user authentication [https://github.com/BarryCarlyon/twitch\_misc/tree/main/authentication/user\_access\_generator/php](https://github.com/BarryCarlyon/twitch_misc/tree/main/authentication/user_access_generator/php) and if you need to get an “anonomous”/public token for server to server calls, which if you are transitioning from Kraken to Helix seems more likely, then you need an App Access/Client Credentials token [https://github.com/BarryCarlyon/twitch\_misc/tree/main/authentication/app\_access\_tokens/php](https://github.com/BarryCarlyon/twitch_misc/tree/main/authentication/app_access_tokens/php)

So you have a number of issues in your code, firsly the fact that the data in `$curlHeader` should in fact be in `CURLOPT_POSTFIELDS` instead, additionally if this is step 3 of user oAuth, this should be a `CURLOPT_POST`, and right now it’s doing a `GET`. Hence the message you get for a invalidy created `<a href` for step 1 user oAuth. Which is what this code generated, loaded `https://id.twitch.tv/oauth2/authorize` with no paramters, when you are supposed to redirect the user here, (as all the params on step 1 need to be query string).

So, it sounds like you actually need to do is

> **[Getting OAuth Access Tokens](https://dev.twitch.tv/docs/authentication/getting-tokens-oauth/)**
>
> Getting OAuth Access Tokens

Which will give you a token which will let you do the same thing as you were doing under v5. Since the oAuth proceedure hasn’t changed and you don’t have an existing oAuth loop to use.

The PHP code examples for generating such a token can be found here

> <https://github.com/BarryCarlyon/twitch_misc/tree/main/authentication/app_access_tokens/php>
>
> //github.com/BarryCarlyon/twitch\_misc/tree/main/authentication/app\_access\_tokens/php

`generate_and_maintain_token.php` is the most appropriate as it generates and stores a token, then resuses that token until the token is close to expiration. Which is “good practice”. This script can be setup as a cronjob, to automatically handle tokens and store the token in a DB or somewhere useful for your scripts/server calls to use.

Generally you wouldn’t store the token in a `auth.json` it just is in the example for simplicity of the example, and not to give an opinion on where/how to store tokens, as thats up to you.

---

<div class="post-metadata">

**Author:** ![RonWhoCares](https://avatars.discourse-cdn.com/v4/letter/r/df788c/32.png) [@RonWhoCares](https://staging-discuss.dev.twitch.com/u/RonWhoCares)\
**Post date:** [February 28, 2022, 3:35pm UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/3 "2022-02-28T15:35:14Z")

</div>

I am initially trying to create an announcement for my web site to say I am live on Twitch. Does this change your response?

---

<div class="post-metadata">

**Author:** ![BarryCarlyon](https://d1d6mrmdu7ztse.cloudfront.net/user_avatar/staging-discuss.dev.twitch.com/barrycarlyon/32/253_2.png) [@BarryCarlyon](https://staging-discuss.dev.twitch.com/u/BarryCarlyon)\
**Post date:** [February 28, 2022, 3:37pm UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/4 "2022-02-28T15:37:41Z")

</div>

PHP is server side programming.

So you would generally use an App Access/Client Credentials token

Additionally you might want to consider EventSub instead of long polling the API

> **[EventSub](https://dev.twitch.tv/docs/eventsub/)**
>
> EventSub

Then Twitch will tell you when your stream starts (and ends). Then store the data in your DB and do a call to your DB instead of a call to the API.

EventSub (webhook transport) also uses Client Credential tokens for this.

TLDR: No, it doesn’t change my answer.

---

<div class="post-metadata">

**Author:** ![RonWhoCares](https://avatars.discourse-cdn.com/v4/letter/r/df788c/32.png) [@RonWhoCares](https://staging-discuss.dev.twitch.com/u/RonWhoCares)\
**Post date:** [February 28, 2022, 11:22pm UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/5 "2022-02-28T23:22:42Z")

</div>

What you are saying about EventSub makes sense.

I have a new problem. The error message has changed to:

> {“status”:401,“message”:“invalid csrf token”}

Do you have any Twitch API insight as to why this might be occurring?

The current iteration of my PHP code is:

```
<?php

/* file_get_contents replaced with curl function - Add this function and replace any use of file_get_contents with file_get_contents_curl */

function file_get_contents_curl($url) {
    $curlField = [
        'client_id' => '"CLIENT ID"',
        'client_secret' => '"CLIENT SECRET"',
        'grant_type' => 'token',
    ];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_AUTOREFERER, TRUE);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $curlField);
    $data = curl_exec($ch);

    curl_close($ch);
    return $data;
}

print_r( file_get_contents_curl( 'https://id.twitch.tv/oauth2/authorize' ) );

```

---

<div class="post-metadata">

**Author:** ![Dist](https://d1d6mrmdu7ztse.cloudfront.net/user_avatar/staging-discuss.dev.twitch.com/dist/32/7192_2.png) [@Dist](https://staging-discuss.dev.twitch.com/u/Dist)\
**Post date:** [February 28, 2022, 11:56pm UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/6 "2022-02-28T23:56:11Z")

</div>

I’m not a PHP dev, but it looks like you’re attempting to GET the OAuth URL when you should be redirecting the user to that URL so that they’re on Twitch, at which point they can accept/deny the connection to your app and be redirect to your redirect uri.

---

<div class="post-metadata">

**Author:** ![BarryCarlyon](https://d1d6mrmdu7ztse.cloudfront.net/user_avatar/staging-discuss.dev.twitch.com/barrycarlyon/32/253_2.png) [@BarryCarlyon](https://staging-discuss.dev.twitch.com/u/BarryCarlyon)\
**Post date:** [March 1, 2022, 1:01am UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/7 "2022-03-01T01:01:11Z")

</div>

Dist is correct.

For user authentication you need to redirect the user to Twitch.

But you shouldn’t be doing user authentication here. The error returned is what you get when trying to _do things wrong_

You need to be generating a client credentials token as outline here

> **[twitch\_misc/authentication/app\_access\_tokens/php at main ·...](https://github.com/BarryCarlyon/twitch_misc/tree/main/authentication/app_access_tokens/php)**
>
> main/authentication/app\_access\_tokens/php

Full example that chains to a streams lookup, this is semi suitable for a Cron Job. Change the `// stream is live` and not live lines to relevant DB writing calls.

```auto
<?php

define("CLIENT_ID", "");
define("CLIENT_SECRET", "");
define('STREAMER_ID', '');

$keys = false;
if (file_exists( __DIR__. '/auth.json')) {
    $keys = json_decode(file_get_contents( __DIR__. '/auth.json'));
}

$generate_token = true;
if ($keys) {
    // validate the token

    $ch = curl_init('https://id.twitch.tv/oauth2/validate');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Authorization: Bearer ' . $keys->access_token
    ));

    $r = curl_exec($ch);
    $i = curl_getinfo($ch);
    curl_close($ch);

    if ($i['http_code'] == 200) {
        // the token appears good
        $generate_token = false;

        // optional to check the expires
        $data = json_decode($r);
        if (json_last_error() == JSON_ERROR_NONE) {
            if ($data->expires_in < 3600) {
                // less than an hour left
                // make a new token
                echo 'Token close to expire. Regenerate';
                $generate_token = true;
            }
        } else {
            echo 'Failed to parse JSON. Assume dead token';
            $generate_token = true;
        }
    }
}

if ($generate_token) {
    $ch = curl_init('https://id.twitch.tv/oauth2/token');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, array(
        'client_id' => CLIENT_ID,
        'client_secret' => CLIENT_SECRET,
        'grant_type' => "client_credentials"
    ));

    $r = curl_exec($ch);
    $i = curl_getinfo($ch);
    curl_close($ch);

    if ($i['http_code'] == 200) {
        $keys = json_decode($r);
        if (json_last_error() == JSON_ERROR_NONE) {
            // store the token for next run
            file_put_contents( __DIR__. '/auth.json', $r);
        } else {
            echo 'Failed to parse JSON';
        }
    } else {
        echo 'Failed with ' . $i['http_code'] . ' ' . $r;
        exit;
    }
}

    $ch = curl_init('https://api.twitch.tv/helix/streams?user_id='.STREAMER_ID);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Client-ID ' . CLIENT_ID,
        'Authorization: Bearer ' . $keys->access_token
    ));

    $r = curl_exec($ch);
    $i = curl_getinfo($ch);
    curl_close($ch);

    if ($i['http_code'] == 200) {
        $data = json_decode($r);
        if ($data->data && $data->data[0]) {
            // stream is live
        } else {
            // stream is not live
        }
    } else {
        echo 'Failed to get streams ' . $i['http_code'] . ' ' . $r;
    }

```

---

<div class="post-metadata">

**Author:** ![RonWhoCares](https://avatars.discourse-cdn.com/v4/letter/r/df788c/32.png) [@RonWhoCares](https://staging-discuss.dev.twitch.com/u/RonWhoCares)\
**Post date:** [March 1, 2022, 2:55am UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/8 "2022-03-01T02:55:20Z")

</div>

@Dist assumption is wrong. This code will only exist as a server API. This isn’t part of a user interface.

---

<div class="post-metadata">

**Author:** ![Dist](https://d1d6mrmdu7ztse.cloudfront.net/user_avatar/staging-discuss.dev.twitch.com/dist/32/7192_2.png) [@Dist](https://staging-discuss.dev.twitch.com/u/Dist)\
**Post date:** [March 1, 2022, 9:28am UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/9 "2022-03-01T09:28:14Z")

</div>

Then you’re using the wrong OAuth flow, the `token` and `code` flows are for User Tokens and so require sending the user to Twitch.

As Barry linked to you, you need to properly implement the `client_credentials` flow for an App token that can be used entirely server-side.

---

<div class="post-metadata">

**Author:** ![RonWhoCares](https://avatars.discourse-cdn.com/v4/letter/r/df788c/32.png) [@RonWhoCares](https://staging-discuss.dev.twitch.com/u/RonWhoCares)\
**Post date:** [March 1, 2022, 11:23am UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/10 "2022-03-01T11:23:23Z")

</div>

I am looking at [https://dev.twitch.tv/docs/authentication/getting-tokens-oauth/#oauth-client-credentials-flow](https://staging-discuss.dev.twitch.com/t/oauth-client-credentials-flow/25392) and [Barry’s GIT ‘Notes’](https://github.com/BarryCarlyon/twitch_misc/tree/main/authentication/user_access_generator/php#notes). Both are saying to use the URL [https://id.twitch.tv/oauth2/token](https://id.twitch.tv/oauth2/token) to get an access token. When I try this in my code and browser I get an error **404 page not found**.

I did use the “Provide Feedback For This Page” link in an attempt to report this. Without this access token I don’t think I can get started. Am I right? Did the Twitch API developers break something in the removal of their API V5?

Overall what you are saying is starting to make sense to me.

---

<div class="post-metadata">

**Author:** ![Dist](https://d1d6mrmdu7ztse.cloudfront.net/user_avatar/staging-discuss.dev.twitch.com/dist/32/7192_2.png) [@Dist](https://staging-discuss.dev.twitch.com/u/Dist)\
**Post date:** [March 1, 2022, 11:59am UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/11 "2022-03-01T11:59:26Z")

</div>

> [@RonWhoCares](#):
>
> Both are saying to use the URL [https://id.twitch.tv/oauth2/token](https://id.twitch.tv/oauth2/token) to get an access token. When I try this in my code and browser I get an error **404 page not found**.

For the Client Credentials flow to get an App token you don’t actually go to the URL. As shown in the documentation you make a POST request. You’re getting a 404 because you’re trying to a go to a URL in a browser that you shouldn’t be going to.

---

<div class="post-metadata">

**Author:** ![RonWhoCares](https://avatars.discourse-cdn.com/v4/letter/r/df788c/32.png) [@RonWhoCares](https://staging-discuss.dev.twitch.com/u/RonWhoCares)\
**Post date:** [March 1, 2022, 12:26pm UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/12 "2022-03-01T12:26:24Z")

</div>

Okay. I think I am think I am down to my last question or two:

What is the “[scope](https://dev.twitch.tv/docs/authentication/#scopes)” I need to use for an [EventSub](https://dev.twitch.tv/docs/eventsub) to capture when a stream goes online?

I can easily do the coding to receive the update of going live and the stream ending in the database.

---

<div class="post-metadata">

**Author:** ![Dist](https://d1d6mrmdu7ztse.cloudfront.net/user_avatar/staging-discuss.dev.twitch.com/dist/32/7192_2.png) [@Dist](https://staging-discuss.dev.twitch.com/u/Dist)\
**Post date:** [March 1, 2022, 12:31pm UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/13 "2022-03-01T12:31:52Z")

</div>

You don’t need any scope for stream online, as it’s public information. Plus scopes don’t do anything on App tokens anyway, they’re for User tokens.

---

<div class="post-metadata">

**Author:** ![BarryCarlyon](https://d1d6mrmdu7ztse.cloudfront.net/user_avatar/staging-discuss.dev.twitch.com/barrycarlyon/32/253_2.png) [@BarryCarlyon](https://staging-discuss.dev.twitch.com/u/BarryCarlyon)\
**Post date:** [March 1, 2022, 12:58pm UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/14 "2022-03-01T12:58:24Z")

</div>

> [@RonWhoCares](#):
>
> I am looking at [https://dev.twitch.tv/docs/authentication/getting-tokens-oauth/#oauth-client-credentials-flow](https://staging-discuss.dev.twitch.com/t/oauth-client-credentials-flow/25392) and [Barry’s GIT ‘Notes’](https://github.com/BarryCarlyon/twitch_misc/tree/main/authentication/user_access_generator/php#notes). Both are saying to use the URL [https://id.twitch.tv/oauth2/token](https://id.twitch.tv/oauth2/token) to get an access token. When I try this in my code and browser I get an error **404 page not found**.

You missed the part where I pointed out the different oAuth flows

> [@BarryCarlyon](#):
>
> This is a complete example of user authentication [https://github.com/BarryCarlyon/twitch\_misc/tree/main/authentication/user\_access\_generator/php](https://github.com/BarryCarlyon/twitch_misc/tree/main/authentication/user_access_generator/php) and if you need to get an “anonomous”/public token for server to server calls, which if you are transitioning from Kraken to Helix seems more likely, then you need an App Access/Client Credentials token [https://github.com/BarryCarlyon/twitch\_misc/tree/main/authentication/app\_access\_tokens/php](https://github.com/BarryCarlyon/twitch_misc/tree/main/authentication/app_access_tokens/php)

Where I said you likely need the last link, not the first link.

So you mixed up on what I was telling you. Apologies for the lack of clarity

---

<div class="post-metadata">

**Author:** ![RonWhoCares](https://avatars.discourse-cdn.com/v4/letter/r/df788c/32.png) [@RonWhoCares](https://staging-discuss.dev.twitch.com/u/RonWhoCares)\
**Post date:** [March 1, 2022, 2:18pm UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/15 "2022-03-01T14:18:54Z")

</div>

Under the heading “[How To Refresh](https://dev.twitch.tv/docs/authentication)” **the sample response on success** doesn’t show how many seconds the new token is in effect for. Am I missing this?

---

<div class="post-metadata">

**Author:** ![BarryCarlyon](https://d1d6mrmdu7ztse.cloudfront.net/user_avatar/staging-discuss.dev.twitch.com/barrycarlyon/32/253_2.png) [@BarryCarlyon](https://staging-discuss.dev.twitch.com/u/BarryCarlyon)\
**Post date:** [March 1, 2022, 3:41pm UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/16 "2022-03-01T15:41:19Z")

</div>

> [@RonWhoCares](#):
>
> doesn’t show how many seconds the new token is in effect fo

The current refresh is retuend in the JSON payload that contains the Access Token itself.

Additionally App Access Tokens cannot be refreshed, you jsut get a new one

See also validating tokens [Authentication | Twitch Developers](https://dev.twitch.tv/docs/authentication#validating-requests)

---

<div class="post-metadata">

**Author:** ![system](https://d2spem0quai2ol.cloudfront.net/original/2X/0/05909729875cce3b53e59d5561c4d23bb14948fc.png) [@system](https://staging-discuss.dev.twitch.com/u/system)\
**Post date:** [March 31, 2022, 3:41pm UTC](https://staging-discuss.dev.twitch.com/t/unable-to-get-twitch-api-oauth-for-php/37113/17 "2022-03-31T15:41:19Z")

</div>

Closed on March 31, 2022, 3:41pm UTC

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
