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

YouTube OAuth2 demo #141

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

YouTube OAuth2 demo #141

wants to merge 2 commits into from

Conversation

ryanckulp
Copy link
Owner

from a live coding session at Camp, Cohort 3.

what is OAuth2?

when Users want to connect your app (App 1) with another app (App 2), you need access to their data in App 2. if App 2 has an API, you need the User to grant you permission to their data with some form of API key.

the most common way to provide this API key was for App 2 to let users visit a "Developer" area in their account settings where they could generate and copy/paste a key into 3rd party tools, such as App 1 (your product). but this has several challenges.

  1. if App 2 only allows for 1 API key, you can't scope access between 3rd party products
  2. if you wish to revoke access to App 2 from a 3rd party app (say, App 3), then App 1 will also lose access
  3. copy/pasting API keys has a potential for user error, and App 1 always has to keep track of where App 2 manages keys
  4. in some cases, neither App 1 nor App 2 want the User to have access to their own API key

OAuth(2) addresses all these issues and more. Oauth is an authorization + authentication strategy that makes it a) easy for Users to connect applications and b) more secure + robust from a connection management and logging perspective.

How Oauth2 works

below is a simple flow of data between your application and the application on which you build an OAuth "client," which then enables your Users to share the other application's data with yours.

in this example we're building an app called "VideoGoals," which allows a YouTube creator to connect their channel and set goals for their content, such as likes or view counts on a per video basis.

Oauth2 flow example

Steps to building OAuth connections

  1. create Oauth2 Client + configure scopes (permissions)
  2. form an 'authorize url'
  3. configure the callback to swap the "code" for an API Key

Setting up an Oauth2 client

in this example we visit cloud.google.com, create a project, enable the "YouTube Data v3" api, and select the scope https://www.googleapis.com/auth/youtube.

on the OAuth consent screen we set our redirect URI to simply http://localhost:3000, then are granted a "client ID" and "client secret" which we'll need later.

a quick search for "Google OAuth2 authorize URL" reveals this snippet:

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly&
 access_type=offline&
 include_granted_scopes=true&
 response_type=code&
 state=state_parameter_passthrough_value&
 redirect_uri=https%3A//oauth2.example.com/code&
 client_id=client_id

simply swap out the scope, redirect_uri, and client_id from what you configured inside the Google Cloud UI, and visit this URL in your web browser.

this completes Step 1 of the diagram above, without writing any code.

it's worth noting that Google's cloud platform and OAuth2 client generator is much more complex than most. to check out simpler examples of OAuth clients, finding and setting scopes, and managing oauth consent screens, check out Gumroad and HubSpot's docs:

one of the most common issue with setting up OAuth clients is simply not paying attention to detail. it's very easy to have a small typo in your authorize URL that will prevent you from testing anything. an example is setting multiple scopes (permissions). some OAuth2 implementations require space delimited, others encoded (%20), others with commas, and so on like below:

image

Creating access tokens

after visiting a (working) authorization URL and following through the prompts (login + accept scopes), the User will be redirected to your "redirect URI," which in our examples above is simply localhost:3000. note that you do not need to have an actual server running, or any backend code configured at this point.

upon redirection your web browser will look something like this:

image

we want to extract the code= parameter, which in this example is sdfjasaskdfjsadkfsadf.

next, send a POST request to the "token" endpoint of your target Oauth2 client. for Google products it looks like this:

  require 'httparty'
  url = "https://oauth2.googleapis.com/token"

  body = {
    code: 'sdfjasaskdfjsadkfsadf',
    client_id: 'YOUR_CLIENT_ID_FROM_OAUTH2_CLIENT_DEVELOPER_PORTAL',
    client_secret: 'YOUR_CLIENT_SECRET_FROM_OAUTH2_CLIENT_DEVELOPER_PORTAL',
    redirect_uri: 'http://localhost:3000',
    grant_type: 'authorization_code'
  }

  resp = HTTParty.post(url, body: body)
  data = JSON.parse(resp.body)

the parsed JSON will look like this, and contain a valid access token with which to make API requests against that User's data on the target application.

  {"access_token"=>"ya29.b0Ad52N3-Fz2Na87OyRA8H0CHKHSbuurW4nM", "expires_in"=>3599, "refresh_token"=>"1//05XHw8RszAg5CCgYIArBAGaYSNwF", "scope"=>"https://www.googleapis.com/auth/youtube", "token_type"=>"Bearer"}

Making API requests

the Oauth2 standard is for access tokens to be included in API requests via a header called "authorization" and prefixed with "Bearer", like so:

headers = {
  .. other headers here, if applicable
  'authorization' => "Bearer ya29.b0Ad52N3-Fz2Na87OyRA8H0CHKHSbuurW4nM"
}

Refreshing access tokens

yet another OAuth2 standard is for access tokens to expire after some amount of time. as you can see in the "data" response above, Google tokens expire in 3600 seconds (1 hour). to request data from a User's account after the expiration, simply refresh the token like so:

require 'httparty'

url = "https://oauth2.googleapis.com/token"
body = {
      client_id: 'YOUR_CLIENT_ID_FROM_OAUTH2_CLIENT_DEVELOPER_PORTAL',
      client_secret: 'YOUR_CLIENT_SECRET_FROM_OAUTH2_CLIENT_DEVELOPER_PORTAL',
      refresh_token: '1//05XHw8RszAg5CCgYIArBAGaYSNwF',
      grant_type: grant_type
    }

    resp = HTTParty.post(url, body: body)

refreshing access tokens is done with the "refresh token" from earlier, when you exchanged the code for an access token. always save refresh tokens, because without them your access tokens will eventually be useless.

note that refresh tokens typically don't change, only access tokens. this is a debate amongst some engineers who claim that expiring access tokens is essentially a useless security feature, since refresh tokens act as permanent access tokens.

Next steps

see the code sample in this PR for an automated approach to swapping redirect URI "code" values for working access tokens.

@ryanckulp
Copy link
Owner Author

Below are live session notes from a student in attendance:

OAuth

  • OAuth 1: Magento, older versions
  • OAuth 2: New version commonly used

Common errors in OAuth 1 solved by OAuth 2

  • Pasting in API Key -> User error
  • Giving application data forever
  • Deleting access to API key breaks things

OAuth 2 changes

  1. Grant explicit permission to APIs
  2. Scopes: permissions - gives only access to certain information with read or write. Customer names, phone, address would be accessible forever, even after user delete their data
  3. Security: API keys by default expire after set time (24 hours usually, 1 month max)
    Instead of pinging data every time, OAuth 2 needs to refresh API token if account is in good standing. Ex: If I reset Gmail password, can break connection to OAuth -> can be good news if I am hacked and you can’t refresh token to corrupt my data
  4. User experience: Gross to copy>paste keys and give them instructions to navigate them. Always stay in tune with particular API instructions

Historical context

  • Back in the day, Facebook apps would break when users chose scopes, but apps changed it from consumer choice to developer
  • Nowadays it's common from Developer to apply for scopes. Once they get permission, user can only accept all of the scopes. Scopes are all or nothing.

Sensitive vs. non-sensitive scopes

  • Google or Youtube might have to approve your app

Naming - why is it important?

  • Multiple clients of app
  • Localhost / local dev
  • Staging server
  • Production
  • One set of master credentials with the same scope
  • Always create one for dev and one for production

Javascript origins - not typical OAuth (a Google thing)

  • Lets pop-up boxes (Oauth consent screens/modals) only be opened from particular domains
  • Authorized Redirect URIs (part of the security features in Oauth2)

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

Successfully merging this pull request may close these issues.

1 participant