Git Product home page Git Product logo

oauth2-client's Introduction

OAuth 2.0 Client

This package provides a base for integrating with OAuth 2.0 service providers.

Gitter Chat Source Code Latest Version Software License Build Status Codecov Code Coverage Total Downloads


The OAuth 2.0 login flow, seen commonly around the web in the form of "Connect with Facebook/Google/etc." buttons, is a common integration added to web applications, but it can be tricky and tedious to do right. To help, we've created the league/oauth2-client package, which provides a base for integrating with various OAuth 2.0 providers, without overburdening your application with the concerns of RFC 6749.

This OAuth 2.0 client library will work with any OAuth 2.0 provider that conforms to the OAuth 2.0 Authorization Framework. Out-of-the-box, we provide a GenericProvider class to connect to any service provider that uses Bearer tokens. See our basic usage guide for examples using GenericProvider.

Many service providers provide additional functionality above and beyond the OAuth 2.0 specification. For this reason, you may extend and wrap this library to support additional behavior. There are already many official and third-party provider clients available (e.g., Facebook, GitHub, Google, Instagram, LinkedIn, etc.). If your provider isn't in the list, feel free to add it.

This package is compliant with PSR-1, PSR-2, PSR-4, and PSR-7. If you notice compliance oversights, please send a patch via pull request. If you're interested in contributing to this library, please take a look at our contributing guidelines.

Requirements

We support the following versions of PHP:

  • PHP 8.2
  • PHP 8.1
  • PHP 8.0
  • PHP 7.4
  • PHP 7.3
  • PHP 7.2
  • PHP 7.1
  • PHP 7.0
  • PHP 5.6

Provider Clients

We provide a list of official PHP League provider clients, as well as third-party provider clients.

To build your own provider client, please refer to "Implementing a Provider Client."

Usage

For usage and code examples, check out our basic usage guide.

Contributing

Please see our contributing guidelines for details.

License

The MIT License (MIT). Please see LICENSE for more information.

oauth2-client's People

Contributors

adam-paterson avatar alexbilbie avatar aripringle avatar bajb avatar belphemur avatar bencorlett avatar compwright avatar dependabot[bot] avatar dhrrgn avatar grahamcampbell avatar hajekj avatar jamesmills avatar jasonvarga avatar jeremykendall avatar jildertmiedema avatar jrfnl avatar jzecca avatar kevindierkx avatar msurguy avatar philsturgeon avatar rakeev avatar ramsey avatar rtheunissen avatar sammyk avatar shadowhand avatar stevenmaguire avatar tomhanderson avatar tpavlek avatar vimishor avatar zot24 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

oauth2-client's Issues

Missing response_type when authenticating with Google

I ran into problem when calling $provider->authorize() with php 5.3.10. I kept getting "Missing response_type".

It turns out that the fourth argument, enc_type, used with http_build_query() wasn't available until PHP 5.4.

Would be great if the PHP dependency in composer.json was changed to >=5.4.0

How to integrate the library and use in PyroCMS module

I'm working on a new Social module for PyroCMS and I'll like to use this library. Since PyroCMS don't use, yet, composer then I download the zip file and put in /addons/shared_addons/libraries/oauth2 then I have /src and /test under that route. How I can autoload the library at /system/cms/config/autoload.php and how I can start using the library on my module?

Identity Provider: Github enterprise support?

Hello,

I was wondering if there is support for github enterpise?
I already tried extending github implementation, overwrite the url's.
But I keep getting the same exception:
Required option not passed: access_token

Mar 11 15:52:10 simplesamlphp ERROR [f1e80aee5a] Backtrace:
Mar 11 15:52:10 simplesamlphp ERROR [f1e80aee5a] 4 /home/oauth-dev/vendor/league/oauth2-client/src/League/OAuth2/Client/Token/AccessToken.php:38 (League\OAuth2\Client\Token\AccessToken::__construct)
Mar 11 15:52:10 simplesamlphp ERROR [f1e80aee5a] 3 /home/oauth-dev/vendor/league/oauth2-client/src/League/OAuth2/Client/Grant/Authorizationcode.php:25 (League\OAuth2\Client\Grant\Authorizationcode::handleResponse)
Mar 11 15:52:10 simplesamlphp ERROR [f1e80aee5a] 2 /home/oauth-dev/vendor/league/oauth2-client/src/League/OAuth2/Client/Provider/IdentityProvider.php:137 (League\OAuth2\Client\Provider\IdentityProvider::getAccessToken)

Thank you in advance.

Add support for custom parameters for the authorization url

Google allows the "custom" login_hint parameter when accessing the authorization url for applications to hint at an email address. This is helpful for users if the application knows the email address, the user would like to add Google as OAuth provider, but is not logged in or is logged into multiple accounts at Google.

The IdentityProvider currently doesn't seem to allow custom parameters for the authorization url. It would be a nice-to-have feature, since it does increase usability quite a lot and could be used for other non-official parameters (for example Google's approval_prompt and Facebook's display).

To add this, one could either

  1. allow custom parameters to be passed in the options array or as separate parameter to IdentityProvider::getAuthorizationUrl or
  2. allow IdentityProviders to specifiy custom parameters for the authorization url (which is probably the nicer solution, since then parameters cannot be completely arbitrary).

A bug in IdentityProvider.php getAccessToken function

getAccessToken($grant = 'authorization_code', $params = array())
{
if (is_string($grant)) {
$grantPath = 'League\OAuth2\Client\Grant'.ucfirst(str_replace('_', '', $grant));

        if ( ! class_exists($grantPath)) {
            throw new \InvalidArgumentException('Unknown grant "'.$grant.'"');
        }

        $grantObj = new $grantPath;
    } elseif ( ! $grant instanceof Grant\GrantInterface) {
        throw new \InvalidArgumentException($grant.' is not an instance of \OAuth2\Client\Grant\GrantInterface');
    }

    $defaultParams = array(
        'client_id'     => $this->clientId,
        'client_secret' => $this->clientSecret,
        'redirect_uri'  => $this->redirectUri,
        'grant_type'    => $grant,
    );

....
return $grantObj->handleResponse($result);
This is my fix. The problem is the pass in $grant get override by the class path, then become the grant object. Then the grant object assigned to $defaultParams, which fail the whole process. I changed the code to keep original $grant untouched. And use $grantPath for class path. And $grantObj for grant object.

Separating Provider Query Building

Because one query doesn't fit all, thank you facebook, would it be worth putting the query building process into a getAuthorizationParams method with sensible defaults that can be overwritten by any individual provider class?

The reason I'm suggesting this is because of @brunogaspar's comment from this pull request.

"since there is already a "hack" for Google on the parameters array."

By splitting it up you don't need to check if changes break other providers, you get it working once and then can rest assured it still works when you change or add others.

If it's worth doing, I don't mind making a pull-request and doing it myself. I look forward to hearing what you all have to say about this topic.

AuthorizationCode.php file causing errors

I just did a composer update locally and in the composer package, the class is Authorizationcode while the file is camel cases. It appears this is fixed in the repo. I was going to do a pull request, but the code in the repo was different than what I just pulled from composer. Just wanted to give you a heads up. Make sense?

Refresh Access Token

I've written a custom refresh token grant for this package, is that something worth making a pull request over? Or are there plans to add this internally, if at all?

http_build_query optional args

I ran into an issue when pushing the code onto a server cos the ini setting (arg_separator.output) was set to "&". I was using the Facebook provider and because of this the query params were not recognised, past the first one obviously. This was a reputable host (Joyent) so I wondered if this was an issue for others who would not be able to set the ini setting to "&". There are a couple of possible options to solve I think

  1. Add additonal optional args to http_buld_query to ensure correct string output

    In IdentityProvider.php function getAccessToken Ln 106
    Change

     $client = new GuzzleClient($this->urlAccessToken() . '?' .      http_build_query($requestParams));
    

    to

     $client = new GuzzleClient($this->urlAccessToken() . '?' .      http_build_query($requestParams,'','&',PHP_QUERY_RFC1738))));
    
  2. Add in a method to allow people to override the ini setting using ini_set , (probably overkill)

  3. Add documentation to say check setting is &

Thoughts? - is this really an issue.

Happy to do PR for appropriate fixes.

Tests?

Hi,

The tests are missing.

Adding tags

Hi,

I really like this package, but I can't add it as a dependency for my own packagist package. Any chance you can make a version tag of the current master (if it is stabil enough) so I don't need to use dev-master with composer?
Or if someone has any other suggestion on how to do it?

Autoload ability

Hi, thanks for this great library!

How one can/should autoload this lib when cloning it as a sub-module via git? Have searched the code base but did not found any autoload function to invoke when not using composer as dependency manager for instance.

Using the library with three buttons

So I have this three buttons in my page, one for Facebook, Google+ and forget Twitter since it's not supported by this library:

5-29-2014 10-43-14 pm

And I'm trying to integrate your library in a module for PyroCMS where users should be able to login using their OAuth credentials trough Google+ and Facebook, how I should call the providers? I mean where is supposed to do if I click in Facebook button? Which method should I call in order to redirect users to Facebook App and setup permissions and the same for Google+?

Issue with 'expires' logic in AccessToken entity

I am using a custom provider I created to connect to our Oauth2 server. I believe I've identified an issue with the handling of the 'expires' parameter in the AccessToken entity constructor.

It first sets 'expires' by adding the current time to the 'expires_in' response from the server, as it should.

The issue is if the auth server sent back an 'expires' value, the $options['expires'] variable gets set to the current time PLUS the expires time from the auth server. The comment says something about this being facebook's fault.

This causes an issue when the auth server returns an 'expires' time that is an expiration timestamp. The Leauge Oauth2 server (ironically enough) returns both expires_in (seconds until expiration) AND expires (timestamp of expiration). So this issue is causing the oauth client to

I can do a fix and pull request but wanted to get an opinion on how to approach the solution first.

In my opinion, if the issue is with facebook, this should be handled in the facebook provider by adjusting the return variables before passing them on to be hydrated by the entity. The entity should then defer to expires as a true timestamp, or default to expires_in + the current time.

Incorrect User Propertes in Google Class

The userDetails function sets the $user->image property, which doesn't exist. It should be setting the $user->imageUrl property. The same error exists for the firstName and lastName properties.

I also found there was an invalid League\ in a namespace somewhere, though I can't confirm where that was. I ran across it while working with the Google provider.

Workaround to Retrieve Facebook Username

Facebook Graph 2.0 lists username (along with locations, bio) field as deprecated.

But it's nice to have the username while we still can.

// grab username for user {$response->id}
$client->setBaseUrl('http://graph.facebook.com/' . $response->id);
$request = $client->get()->send();
$info = $request->json();
$username = $info['username'];

// one shot
$username = json_decode(file_get_contents('http://graph.facebook.com/'.$data->uid))->username

Make error_description and error_uri accessible

The OAuth2 spec allows for error_description and error_uri parameters in the error response (http://tools.ietf.org/html/rfc6749#section-5.2). The current implementation of IDPException doesn't expose these at all.

These details could arguably be added to the exception message, but I could see a use case for keeping the message with just the "error" key so it can be easily parsed. Would it make sense to add getErrorDescription and getErrorUri methods? Or perhaps just a getResult method that provides access to the $result property?

Add Twitter provider ?

I was actually surprised this provider wasn't built-in, it this because of implementations differences between Twitter and the other ones or could it be easily added ?

Keep session

Hi,

How can I keep session (like in cookies) on client slide. If I made authentication mode with remember me parameter, what I should change in configuration for that. Default Laravel configuration Session is file type with lifetime 120minutes.

I tried to configure Guzzle cookie but without success.

Laravel

How can I add this to Laravel 4. Do I need to update my composer.json file and if so, how would that look like?

I'm trying to find a simple oauth2 library to sign in with google.

Add Support for Google Refresh Token Use

For anyone interested in adding support for Google refresh tokens, there are a few changes you have to make:

  1. Add a new class in the Client\Grant folder named Refreshtoken
<?php

namespace OAuth2\Client\Grant;
use OAuth2\Client\Token\AccessToken as AccessToken;

class Refreshtoken implements GrantInterface {

    public function __toString()
    {
        return 'refresh_token';
    }

    public function prepRequestParams($defaultParams, $params)
    {
        if ( ! isset($params['refresh_token']) || empty($params['refresh_token'])) {
            throw new \BadMethodCallException('Missing refresh token');
        }

        return array_merge($defaultParams, $params);
    }

    public function handleResponse($response = array())
    {
        return new AccessToken($response);
    }
}
  1. Modify the getAccessToken function of Client\Provider\IdentityProvider:
    public function getAccessToken($grant = 'authorization_code', $params = array())
    {
        $grantType = $grant;

        if (is_string($grant)) {
            $grant = 'OAuth2\\Client\\Grant\\'.ucfirst(str_replace('_', '', $grant));
            if ( ! class_exists($grant)) {
                throw new \InvalidArgumentException('Unknown grant "'.$grant.'"');
            }
            $grant = new $grant;
        } elseif ( ! $grant instanceof Grant\GrantInterface) {
            throw new \InvalidArgumentException($grant.' is not an instance of \OAuth2\Client\Grant\GrantInterface');
        }

        $defaultParams = array(
            'client_id'     => $this->clientId,
            'client_secret' => $this->clientSecret,
            'grant_type'    => $grant,
        );

        if ($grantType == 'authorization_code') {
            $redirectParams = array('redirect_uri' => $this->redirectUri);
            $defaultParams = array_merge($defaultParams, $redirectParams);
        }

        $requestParams = $grant->prepRequestParams($defaultParams, $params);

        try {
            switch ($this->method) {
                case 'get':
                    $client = new GuzzleClient($this->urlAccessToken() . '?' . http_build_query($requestParams));
                    $request = $client->send();
                    $response = $request->getBody();
                    break;
                case 'post':
                    $client = new GuzzleClient($this->urlAccessToken());
                    $request = $client->post(null, null, $requestParams)->send();
                    $response = $request->getBody();
                    break;
            }
        } catch (\Guzzle\Http\Exception\BadResponseException $e) {
            $raw_response = explode("\n", $e->getResponse());
            $response = end($raw_response);
        }

        switch ($this->responseType) {
            case 'json':
                $result = json_decode($response, true);
                break;
            case 'string':
                parse_str($response, $result);
                break;
        }

        if (isset($result['error']) && ! empty($result['error'])) {
            throw new IDPException($result);
        }

        return $grant->handleResponse($result);
    }
  1. Fix the bug in the Client\Provider\Google (see my other issue related to this item)

  2. Update the authorize function of Client\Provider\IdentityProvider to add the access type variable:

$params = array(
            'client_id' => $this->clientId,
            'redirect_uri' => $this->redirectUri,
            'state' => $state,
            'scope' => is_array($this->scopes) ? implode($this->scopeSeperator, $this->scopes) : $this->scopes,
            'response_type' => isset($options['response_type']) ? $options['response_type'] : 'code',
            'access_type' => 'offline',
            'approval_prompt' => 'force' // - google force-recheck
        );
  1. Use the provider to get the access token in much the same way you would normally:
$token = $provider->getAccessToken('refresh_token', array('refresh_token' => $user->refreshToken));

Problems adding DropBox as a provider

I tried to add DropBox as a provider.

Here is my code: https://gist.github.com/jnbdz/7624654

I was actually able to solve the problem quickly, but I think this is something you guys should think about for your next version.

To make DropBox work I had to comment out line 70:

//'approval_prompt' => 'force' // - google force-recheck

The problem then is if I want to use Google it won't work anymore...

Any prevision for LinkedIn or Twitter?

First of all thank you for this awesome Library. I have used it today for the first time and was incredibly easy to login with all the 3 providers. Any prevision for LinkedIn or Twitter?

Problem with callback

Hello.
Following is authenticated through Google, I'm going to call back the action and I get the error: Unknown grant "League\OAuth2\Client\Grant\Authorizationcode"
In file /.../league/oauth2-client/src/Provider/AbstractProvider.php:113
A couple of weeks everything worked, but after the upgrade there is a problem.
Please, help me solved this problem.

exit on authorize result in session lost in laravel

this is a very interesting case. when we do oauth we tend to set a session key in laravel that indicate the user initiate an oauth.

but thing is, if exit is called, such session key won't be saved. I am pretty sure this is a laravel thing, but may also be a general php feature?

nonetheless the easier solution is to avoid calling exit on authorize right?

see ticket laravel/framework#3267 for reference

Getting bad server error

$provider = new League\OAuth2\Client\Provider\Lis(array(
'clientId' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'clientSecret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'redirectUri' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
));

Error msg.

Oops! An Error Occurred

The server returned a "500 Internal Server Error".

Something is broken. Please e-mail us at [email] and let us know what you were doing when this error occurred. We will fix it as soon as possible. Sorry for any inconvenience caused.

I've been given this library to communicate with an oauth server implented using this oauth server.

I think i got the following params right.

Add 0.2 to packagist

Wondering if you'd be willing to add the 0.2 tag to packagist so that I don't have to require "dev-master" in a package that depends on yours. Thanks! :)

file_get_content or cURL?

Hi

I can't find into code which method you use for connect with provider, cURL or file_get_content?

Thanks

getAccessToken() fails due to case sensitivity of class name/file

On my local and remote linux system (case sensitive systems) $IdentityProvider->getAccessToken('authorization_code') fails, due to

$grant = 'League\\OAuth2\\Client\\Grant\\'.ucfirst(str_replace('_', '', $grant));

(in Provider/IdentityProvider:108) not beeing able to find and autoload the "Authorizationcode" class. Upon checking the file, class name and file name are inconsistent, file name is "AuthorizationCode.php" while the class name is "Authorizationcode".

This might work on Windows systems, but since many servers are Unix/Linux this should be fixed. Class names should be consistent within the file and the filenames to prevent these kind of issues from happening.

Either the file should be named "Authorizationcode.php" or the class should be renamed to "AuthorizationCode" and the upper code piece reworked.

Editing Scopes

Hello,

First of all, congratulation for your package, I tried several oauth packages and yours is very accessible.

I'd like to know if there is a clean way to overwrite the default scope? I mean I could modify directly the files in the vendors (I'm using Symfony2) but I'd prefer not to touch them. Other option would be to extend the class?

Do you have any other idea? Something where the user could just add an option when throwing $provider->getAuthorizationUrl() and it would overwrite default config?

Thanks !

GrantInterface Exception always false

The elseif statement checking if the $grant is an instance of Grant\GrantInterface always returns false since Grant\GrantInterface isn't even available.

Im talking about the following snippet:

} elseif ( ! $grant instanceof Grant\GrantInterface) {
    throw new \InvalidArgumentException($grant.' is not an instance of \OAuth2\Client\Grant\GrantInterface');
}

Replacing Grant\GrantInterface with \OAuth2\Client\Grant\GrantInterface resolves the issue.
Is this correct or am I doing something wrong here?

Authorize does not validate state parameter

So I noticed that when authorizing a a code we do not validate that the state returned matches that generated within getAuthorizationUrl.

You see we generate the code here

    public function getAuthorizationUrl($options = array())
    {
        $state = md5(uniqid(rand(), true));
        setcookie($this->name.'_authorize_state', $state);

but within getAccessToken we do not validate it.

Was there any reason for this or was it just forgotten ?

Needs a license

Needs a license file adding to the code as well as copyright info and acknowledgements to JISC and university of Lincoln (same as server code).

Not getting all the information from Github API.

For some reason I cannot get the email of my GitHub.

I tried to dig in the code did not find anything.

I even added the scope:

public $scopes = array('user:email');

I also tried:

public $scopes = array('user');

I get NULL.

It also seems that other information are not loading:

string(3) "uid"
int(--------) -> this works but I hid it.
string(8) "nickname"
string(5) "jnbdz"
string(4) "name"
string(19) "Jean-Nicolas Boulay"
string(9) "firstName"
NULL
string(8) "lastName"
NULL
string(5) "email"
NULL
string(8) "location"
NULL
string(11) "description"
NULL
string(8) "imageUrl"
NULL
string(4) "urls"
array(2) {
  ["GitHub"]=>
  string(18) "http://github.com/"
  ["Blog"]=>
  NULL
 }

Seperation of Concerns, User Data and Scopes

Hi,

amongst several Providers, i'm using the Google Provider. However i have different Scope Requirements and do not need the scope https://www.googleapis.com/auth/userinfo.email.

However overwriting the Scope with my required Scope, results in the the exception Undefined index: email on getUserDetails

My Question is how should i handle this? is see several options:

  1. Extend the GoogleProvider, overwriting the getUserDetails function
  2. implementing a "bare google Provider" extending the AbstractProvider, resulting in lots of duplicate configuration code.
  3. forking the repo and modifiying the few crucial Lines

I think this boils down to another problem: one of seperation of concerns: As is, there seems to be no way to use the basic oAuth functions, w/o having to come across the League\OAuth2\Client\Entity\User Class. Getting started with oAuth, i appreciate the simplicity of having the Class and it's associated Functions in the Providers (getUserDetails), but in an advanced usage, especially with google providing access to lots of API endponts that have nothing todo with user data, i'd rather have something like a normalizedUserCapableProvider extending the functionality of an AbstractProvider and keeping anything related to a concrete type of endpoint data (read userdata) out of the AbstractProvider.

README example uses wrong property names for access token properties

The current README shows that you can access the property names of an access token object by using:

// Use this to interact with an API on the users behalf
echo $token->access_token;

// Use this to get a new access token if the old one expires
echo $token->refresh_token;

// Number of seconds until the access token will expire, and need refreshing
echo $token->expires_in;

but the actual property names would need to be

$token->accessToken;
$token->refreshToken;
$token->expires;

Namespace is wrong for Laravel 4

I have been trying out this repo with Laravel 4.

I was able to make it work. But I had to modify some of the code.

Everywhere this "\OAuth2[...]" appears I had to add "\League" to it to finally get the code to work.

I don't know if this is a mistake or it's just Laravel 4.

Anyways, it's an easy fix.

Login via Twitter

Hi,
do you have any plan to add support for Twitter ?
Your oauth2-client is great, but i need Twitter :-)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.