Git Product home page Git Product logo

aioauth-client's Introduction

AIOAuth Client

AIOAuth Client -- OAuth support for Asyncio / Trio libraries.

Tests Status

PYPI Version

Python Versions

Requirements

  • python >= 3.8

Installation

AIOAuth Client should be installed using pip: :

pip install aioauth-client

Usage

# OAuth2
from aioauth_client import GithubClient

github = GithubClient(
    client_id='b6281b6fe88fa4c313e6',
    client_secret='21ff23d9f1cad775daee6a38d230e1ee05b04f7c',
)

authorize_url = github.get_authorize_url(scope="user:email")

# ...
# Reload client to authorize_url and get code
# ...

otoken, _ = await github.get_access_token(code)

# Save the token for later use

# ...

github = GithubClient(
    client_id='b6281b6fe88fa4c313e6',
    client_secret='21ff23d9f1cad775daee6a38d230e1ee05b04f7c',
    access_token=otoken,
)

# Or you can use this if you have initilized client already
# github.access_token = otoken

response = await github.request('GET', 'user')
user_info = await response.json()
# OAuth1
from aioauth_client import TwitterClient

twitter = TwitterClient(
    consumer_key='J8MoJG4bQ9gcmGh8H7XhMg',
    consumer_secret='7WAscbSy65GmiVOvMU5EBYn5z80fhQkcFWSLMJJu4',
)

request_token, _ = await twitter.get_request_token()

authorize_url = twitter.get_authorize_url(request_token)
print("Open",authorize_url,"in a browser")
# ...
# Reload client to authorize_url and get oauth_verifier
# ...
print("PIN code:")
oauth_verifier = input()
oauth_token, data = await twitter.get_access_token(oauth_verifier)
oauth_token_secret = data.get('oauth_token_secret')

# Save the tokens for later use

# ...

twitter = TwitterClient(
    consumer_key='J8MoJG4bQ9gcmGh8H7XhMg',
    consumer_secret='7WAscbSy65GmiVOvMU5EBYn5z80fhQkcFWSLMJJu4',
    oauth_token=oauth_token,
    oauth_token_secret=oauth_token_secret,
)

# Or you can use this if you have initilized client already
# twitter.access_token = oauth_token
# twitter.access_token_secret = oauth_token_secret

timeline = await twitter.request('GET', 'statuses/home_timeline.json')
content = await timeline.read()
print(content)

Example

Run example with command: :

make example

Open http://localhost:8080 in your browser.

Bug tracker

If you have any suggestions, bug reports or annoyances please report them to the issue tracker at https://github.com/klen/aioauth-client/issues

Contributing

Development of AIOAuth Client happens at: https://github.com/klen/aioauth-client

License

Licensed under a MIT license.

aioauth-client's People

Contributors

arthurdarcet avatar butla avatar chermnyx avatar d0ugal avatar dmrz avatar ei-grad avatar gbtami avatar hf-kklein avatar iamraa avatar ivanfeanor avatar julien-duponchelle avatar klen avatar lefcha avatar mrdon avatar nebularazer avatar nhumrich avatar nicfit avatar olegbuevich avatar pbabics avatar peymanmortazavi avatar pyup-bot avatar qrbaker avatar stillinbeta avatar syphdias avatar syrkuit avatar titouanc 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

aioauth-client's Issues

Vkontakte

Hello.
How about this popular socila network?

Refactoring

Hello! Thank you for the library, it became very handy and I enjoyed using it.
What do you think about little refactoring of the code I've seen while using your library:

I didn't enjoy to re-instantiate the Clients everytime I've got an access token. That's why I fixed this. Would you like me to PR?

OPTIONAL: the API you're using for requests here is outdated, so I believe I could refactor it from
response = yield from self.request('POST', self.access_token_url, data=payload, loop=loop) to async with aiohttp.ClientSession() as session: async with session.post(self.access_token_url, data=payload) as resp: syntax and use async/await syntax instead of old one, but it'll break compatibility with older Python versions. On the other hand, aiohttp's API is changing frequently so maybe we should move on to be up-to-date with them?

Non pin based user authentication for Twitter?

Hello thanks for this - do you have a sample (or is it not possible) to have the user login via this module without resorting to the pin based authorization? The Github sample is perfect - as a user I can login directly in the browser, which is what I need for my app. I've tried reworking the Twitter sample but can't quite get it working. Anybody have a sample for this? Thanks!

Cannot use querystring parameters with TwitterClient

Given this code:

t = TwitterClient(consumer_key=TWITTER_CONSUMER_KEY,
                       consumer_secret=TWITTER_CONSUMER_SECRET,
                       oauth_token='976766514308067328-aJlndNEpXueYdnds7dEmJrGw3WhLNa1',
                       oauth_token_secret='RpofCLZd72IuzM1FX98Tl7unJx5yZIoUOZJIEqnBF0Hki')

f = await t.request('GET', 'followers/id.json?user_id=6253282&cursor=-1')

I receive this error:

File "/usr/local/lib/python3.6/site-packages/aioauth_client.py", line 233, in request
    'Request parameters should be in the "params" parameter, '
ValueError: Request parameters should be in the "params" parameter, not inlined in the URL

If I were to remove that raise ValueError (in aioauth_client.py), or if I passed params={ 'user_id' : 6253282 } to request('GET', ...), Twitter gets back with error code 34 (Sorry, that page does not exist)

Make it possible to use a different access_token in request()

Add a access_token parameter to the request() method that overwrites the default one.

    def request(self, method, url, headers=None, access_token=None, **aio_kwargs):
        """Request OAuth2 resource."""
        url = self._get_url(url)
        headers = headers or {'Accept': 'application/json'}
        if self.access_token:
            headers['Authorization'] = "Bearer {}".format(access_token or self.access_token)

        return self._request(method, url, headers=headers, **aio_kwargs)

(From DiscordClient)

sensitive credentials included in debug logs

client_id, client_secret codes and Bearer tokens are exposed in debug logs.

These values should be sanitized before logging.

Excerpt from logs (XXX values are obviously my edits):

2022-04-04 21:29:53 Request POST: https://oauth2.googleapis.com/token {'headers': {'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'}, 'params': None, 'data': {'grant_type': 'authorization_code', 'client_id': 'XXXFOOXXX.apps.googleusercontent.com', 'client_secret': 'XXXSECRETXXX', 'code': ['XXXCODEXXX'], 'redirect_uri': 'https://hostname/path'}}
2022-04-04 21:29:53 Request GET: https://www.googleapis.com/userinfo/v2/me {'headers': {'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'Authorization': 'Bearer XXXTOKENXXX'}, 'params': None}

Client credentials grant type support

Hi,
I tried to use this library to connect some custom OAuth2 service, but I noticed it only support Authorization Code grant type.
My service is using Client Credentials grant type, can you support it too?

Thanks!

Percent encoding

Looking for suggestions on how to adjust percent encoding of oauth parameters.
I'm in the process of creating a new client and requests work as long as '/' is not present in some oauth parameters. Has anyone else experienced a situation like this?

I tried overwriting Client._request to manually encoding parameters which are passed into aiohttp.ClientSession next line. It will work as long as '/' character is not present.

kwargs : {'params': {'oauth_consumer_key': 'aaa', 'oauth_nonce': '4a0ff66a0ac3674e6fd2b3b236890fb05dbe8304', 'oauth_signature_method': 'HMAC-SHA1', 'oauth_timestamp': '1551030612', 'oauth_version': '1.0', 'oauth_callback': 'oob', 'oauth_token': 'qbG4My9Lpz5mc5M1afhflTrG0NwOXN%2FH8bDFig4OoEU%3D',

Result: Get request fails with status 401. session.request(method, url, **kwargs) partly decoded the oauth token ? I thought aiohttp.ClientSession does not attempt percent encoding?

OAuth: oauth1 - DEBUG - response url: https://127.0.0.1?oauth_consumer_key=aaa&oauth_nonce=4a0ff66a0ac3674e6fd2b3b236890fb05dbe8304&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1551030612&oauth_version=1.0&oauth_callback=oob&oauth_token=qbG4My9Lpz5mc5M1afhflTrG0NwOXN/H8bDFig4OoEU%3D&oauth_signature=PA2VSz5drOUBWLbfLXiuPE9bB20%3D

https://tools.ietf.org/html/rfc5849#section-3.6
https://tools.ietf.org/html/rfc3986

Exact aiohttp version

Is there any reasons do not use aiohttp>=1.0.5? It's not convenient to install it with --no-deps switch :)

DiscordClient

Hi there — great library. I tried to write a DiscordClient using your examples but could not get it to work. Namely, this is where I have gotten to but it doesn’t parse the userinfo.

Trying to run the example code provided will get the token however throws an error where it gets a 401 while tryimng to access user_info

class DiscordClient(OAuth2Client):
    """Support Discord.
    * Dashboard: https://discordapp.com/developers/applications/me
    * Docs: https://discordapp.com/developers/docs/intro
    * API reference: https://discordapp.com/developers/docs/reference
    """

    access_token_url = 'https://discordapp.com/api/oauth2/token'
    authorize_url = 'https://discordapp.com/api/oauth2/authorize'
    base_url = 'https://discordapp.com/api/'
    name = 'discord'
    user_info_url = 'https://discordapp.com/api/users/@me'

    def __init__(self, *args, **kwargs):
        """Set default scope."""
        super(DiscordClient, self).__init__(*args, **kwargs)
        self.params.setdefault('scope', 'identify')

    @classmethod
    def user_parse(cls, data) -> UserInfo:
        """Parse information from the provider."""
        user = data.get('response', {}).get('user', {})
        return UserInfo(
            id=user.get('id'),
            username=user.get('username'),
            discriminator=user.get('discriminator'),
            avatar=user.get('avatar'),
            verified=user.get('verified'),
            email=user.get('email')
        )

Any help would be appreciated!

Injecting own aiohttp.ClientSession instance explicitly

For now, all requests in OAuth Clients in this package are performed using aiohttp.request function. But there are cases, when I need to use my own ClientSession instance instead of leaving it up to oauth lib - that is useful for both testing and ease of configuration.

So, I imagine it like this:

from aioauth_client import OAuth2Client

from aiohttp import ClientSession

custom_session = ClientSession(custom_setting='custom_value')

client = OAuth2Client(session=custom_session)

One possible case, why users would need it is a client session instance bound to a fake server emulating an API (like here https://github.com/aio-libs/aiohttp/blob/master/examples/fake_server.py#L144), which makes testing some service relying on external API isolated from the actual API easy and honest.

aiohttp deprecation warnings and unpinned aiohttp package

Hi,

The aioauth-client is using aiohttp without the max version being pinned. This is potentially pretty unstable, since aioauth-client is also using deprecated apis.

The requirements.txt (used in setup.py)

aiohttp >= 3.0.0

Per
https://docs.aiohttp.org/en/stable/changes.html#deprecations-and-removals several of the timeout arguments used in aioauth-client are marked as deprecated and do raise DeprecationWarning notices. These are here https://github.com/klen/aioauth-client/blob/develop/aioauth_client.py#L147

    async def _request(self, method, url, loop=None, timeout=None, **kwargs):
        """Make a request through AIOHTTP."""
        session = self.session or aiohttp.ClientSession(
            loop=loop, conn_timeout=timeout, read_timeout=timeout, raise_for_status=True)
        try:
            async with session.request(method, url, **kwargs) as response:
                if 'json' in response.headers.get('CONTENT-TYPE'):
                    data = await response.json()
                else:
                    data = await response.text()
                    data = dict(parse_qsl(data)) or data

                return data

        except asyncio.TimeoutError:
            raise web.HTTPBadRequest(reason='HTTP Timeout')

        finally:
            if not self.session and not session.closed:
                await session.close()

These are slated to be removed in aiohttp 4.0.0 and once that package is released, aioauth-client users will be in a broken state due to the lack of maximum version pinning.

Can the maximum version be capped and/or the deprecated features be removed and replaced with a single timeout?

Bitbucket2Client bug

 File "/home/tamas/.local/lib/python3.6/site-packages/aioauth_client.py", line 173, in user_info
   data = await self.request('GET', self.user_info_url, loop=loop, **kwargs)
TypeError: object tuple can't be used in 'await' expression

Twitter Streaming API

Thanks for this useful module! Is there a plan to support Twitter's Streaming API?

GitHub example in README.rst seems broken

First off, thank you for this library. But, I have a question :)

When executing the code in:

https://github.com/klen/aioauth-client/blob/develop/README.rst#usage

There seems to be an issue:

user_info = yield from response.json()

The response object is a dict, not the response object: https://github.com/klen/aioauth-client/blob/develop/aioauth_client.py#L147

A simple fix is by doing this, of course:

    response = await github.request('GET', 'user')
    print(response)

Similar issue seems to be in the example:
https://github.com/klen/aioauth-client/blob/develop/example/app.py#L98

There it reads:

    response = yield from github.request('GET', 'user')
    body = yield from response.read()

Which too fails, because response is a dict.

Of course these are all easy to fix, but I was mostly wondering if it was intended, as I too expected the response object as return value. Mainly, as that allows for checking the status field in the request, to see if the request went through okay. It would also give access to GitHub headers indicating the rate limiter current status, etc. Of course with the access_token it is easily enough to make that request directly via aiohttp myself, but it just made me wonder why this decision?

Thank you again for the library, saved me a bit of time :)

Example app on localhost failed with SSL error

Hi,
I am trying to run the example code from this link as it is
https://github.com/klen/aioauth-client/blob/develop/example/app.py

After authorizing the app from Github I get the below error.

aiohttp.client_exceptions.ClientConnectorCertificateError: Cannot connect to host github.com:443 ssl:True [SSLCertVerificationError: (1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)')]

Complete Traceback

/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/bin/python /Users/sureshkl/PycharmProjects/Learn_aiohttp/example/app.py
/Users/sureshkl/PycharmProjects/Learn_aiohttp/example/app.py:160: DeprecationWarning: Application.make_handler(...) is deprecated, use AppRunner API instead
  f = loop.create_server(app.make_handler(), '127.0.0.1', 5000)
serving on ('127.0.0.1', 5000)
SSL handshake failed on verifying the certificate
protocol: <asyncio.sslproto.SSLProtocol object at 0x1037fd208>
transport: <_SelectorSocketTransport fd=11 read=polling write=<idle, bufsize=0>>
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/sslproto.py", line 625, in _on_handshake_complete
    raise handshake_exc
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/sslproto.py", line 189, in feed_ssldata
    self._sslobj.do_handshake()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py", line 763, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)
SSL error in data received
protocol: <asyncio.sslproto.SSLProtocol object at 0x1037fd208>
transport: <_SelectorSocketTransport closing fd=11 read=idle write=<idle, bufsize=0>>
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/sslproto.py", line 526, in data_received
    ssldata, appdata = self._sslpipe.feed_ssldata(data)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/sslproto.py", line 189, in feed_ssldata
    self._sslobj.do_handshake()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py", line 763, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)
Error handling request
Traceback (most recent call last):
  File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/connector.py", line 936, in _wrap_create_connection
    return await self._loop.create_connection(*args, **kwargs)  # type: ignore  # noqa
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/base_events.py", line 986, in create_connection
    ssl_handshake_timeout=ssl_handshake_timeout)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/base_events.py", line 1014, in _create_connection_transport
    await waiter
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/sslproto.py", line 526, in data_received
    ssldata, appdata = self._sslpipe.feed_ssldata(data)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/sslproto.py", line 189, in feed_ssldata
    self._sslobj.do_handshake()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py", line 763, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/web_protocol.py", line 418, in start
    resp = await task
  File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/web_app.py", line 458, in _handle
    resp = await handler(request)
  File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/example/app.py", line 133, in oauth
    _, meta = await client.get_access_token(request.query)
  File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aioauth_client.py", line 340, in get_access_token
    data = await self.request('POST', self.access_token_url, data=payload, loop=loop)
  File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aioauth_client.py", line 152, in _request
    async with session.request(method, url, **kwargs) as response:
  File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/client.py", line 1012, in __aenter__
    self._resp = await self._coro
  File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/client.py", line 483, in _request
    timeout=real_timeout
  File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/connector.py", line 523, in connect
    proto = await self._create_connection(req, traces, timeout)
  File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/connector.py", line 859, in _create_connection
    req, traces, timeout)
  File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/connector.py", line 1004, in _create_direct_connection
    raise last_exc
  File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/connector.py", line 986, in _create_direct_connection
    req=req, client_error=client_error)
  File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/connector.py", line 939, in _wrap_create_connection
    req.connection_key, exc) from exc
aiohttp.client_exceptions.ClientConnectorCertificateError: Cannot connect to host github.com:443 ssl:True [SSLCertVerificationError: (1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)')]

Environment: macOS 10.15.4
Python 3.7.3

Sending query params with multiple values for the same key

When using the google people batch api.

You have to construct an url like this.
https://people.googleapis.com/v1/people:batchGet?resourceNames=people/12345&resourceNames=people/67890&personFields=photos

This fails when aioauth-client tries to modify the params to add the access token:

File "~/.venv/lib/python3.6/site-packages/aioauth_client.py", line 315, in request
    params[self.access_token_key] = access_token
TypeError: list indices must be integers or slices, not str

No information about errors when getting an OAuth 2 access token

When the server returns an error when getting the access token aioauth just raises the unhelpful web.HTTPBadRequest(reason='Failed to obtain OAuth access token.').

While implementing flows with Facebook and Intercom (I used the OAuth2 client as a base for that) I encountered problems that couldn't be debugged, because I couldn't see the server's response.

I'll submit a PR with a fix in a few moments.

LichessClient throwing AttributeError: 'NoneType' object has no attribute 'get'

When lichess user profile is empty I get:

2019-08-20T12:43:56.631908+00:00 app[web.1]: Traceback (most recent call last):
2019-08-20T12:43:56.631909+00:00 app[web.1]:   File "/app/routes.py", line 84, in login
2019-08-20T12:43:56.631911+00:00 app[web.1]:     user, info = await client.user_info()
2019-08-20T12:43:56.631912+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/aioauth_client.py", line 179, in user_info
2019-08-20T12:43:56.631913+00:00 app[web.1]:     user = User(**dict(self.user_parse(data)))
2019-08-20T12:43:56.631914+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/aioauth_client.py", line 476, in user_parse
2019-08-20T12:43:56.631915+00:00 app[web.1]:     yield 'first_name', data.get('profile').get("firstName")
2019-08-20T12:43:56.631918+00:00 app[web.1]: AttributeError: 'NoneType' object has no attribute 'get'

request always get empty dict

else:
data = await response.text()
data = dict(parse_qsl(data))

I'm trying to use OAuth2Client.request get some resource from own server, but client.request always return empty dict. I found out the problem happens in Client._request.

maybe you can add a parameter like raw_content, let us control how to deal with the response?

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.