GithubHelp home page GithubHelp logo

fenichelar / ember-simple-auth-token Goto Github PK

View Code? Open in Web Editor NEW
350.0 20.0 140.0 2.46 MB

Ember Simple Auth extension that is compatible with token-based authentication like JWT.

Home Page: http://fenichelar.github.io/ember-simple-auth-token/

License: MIT License

JavaScript 97.15% HTML 1.86% CSS 0.03% Handlebars 0.96%
ember javascript jwt ember-addon

ember-simple-auth-token's People

Contributors

abhishek97 avatar alechirsch avatar bekzod avatar calvinmetcalf avatar dcyriller avatar dependabot[bot] avatar digia avatar eluciano11 avatar evoactivity avatar fenichelar avatar gertjanwytynck avatar globegitter avatar homu avatar jakesjews avatar jdhoek avatar jelhan avatar jembezmamy avatar jherdman avatar joshuataylor avatar jpadilla avatar leandrocp avatar leo avatar rbartoli avatar subtletree avatar sukima avatar thhermansen avatar tibotiber avatar validkeys avatar xcambar avatar yusufsagdic 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

ember-simple-auth-token's Issues

Header or body?

Can you explain me please the purpose of using both request header and response body in the token exchange? If i'm not mistaken when the client makes authentication or renew call this library expects the token to be in the body of server response. On the other hand an authorizer puts the token into header of a request. Why not always use header-based approach?

Update:
May be would be better to use more generic approach when taking the token from response or putting into request. It will give a useroptions - put/get the token into the request/response body or user specified header.

Should this still work with the latest ember?

The authenticator doesn't get registered after I run the ember generate simple-auth-token command...

I know there has been a lot of work around the add-ons area of ember-cli and I wonder if this is because of that?

Silent login after user confirm

I've a service where user should confirm his account after signup.
Confirmation is made clicking on a link sent via email. Once the user clicks that link, I confirm his account server side and return a token to the client.
How can I set that token as the authorize token and then silently login the user?
I've tried setting session variables but I wasn't able to 'tell' to simple-auth to use that value as the authorizer token to use in following requests.

Authorization Header possible error in token.js

Hi I noticed that there might be an error in the authorize method.

I had to change this.HeaderName to this.authorizationHeaderName

authorize: function(jqXHR, requestOptions) {
    var token = this.get('session.' + this.tokenPropertyName);
    if (this.get('session.isAuthenticated') && !Ember.isEmpty(token)) {
      if (!isSecureUrl(requestOptions.url)) {
        Ember.Logger.warn('Credentials are transmitted via an insecure connection - use HTTPS to keep them secure.');
      }
      jqXHR.setRequestHeader(this.authorizationHeaderName, this.authorizationPrefix + token);
    }
  }

Controllers

Hi there.
As the controllers are going to be deprecated soon, there's a way to get rid of the login controller?

Thanks

Tokens not refreshing after restore

There's a bug on this line:

https://github.com/jpadilla/ember-cli-simple-auth-token/blob/master/addon/authenticators/jwt.js#L129

_this.scheduleAccessTokenRefresh(dataObject.get(_this.tokenExpireName), token);

dataObject contains the expiry time at the expiresAt key when authenticating. Ref: https://github.com/jpadilla/ember-cli-simple-auth-token/blob/master/addon/authenticators/jwt.js#L176-178

So it's either we retrieve 'expiresAt' from dataObject or retrieve _this.tokenExpireName from tokenData.

Actually, there's a bit of redundant code in here.

  1. The expiry is retrieved incorrectly in https://github.com/jpadilla/ember-cli-simple-auth-token/blob/master/addon/authenticators/jwt.js#L107 (This will always be undefined unless tokenExpireName happens to be 'expiresAt'.
  2. Since expiresAt will always be undefined above, it is correctly retrieved in https://github.com/jpadilla/ember-cli-simple-auth-token/blob/master/addon/authenticators/jwt.js#L113-L121 (This retrieves expiry from tokenData).
  3. The correct expiry retrieved above is now saved in the expiresAt variable, but is not the one passed to scheduleAcessTokenRefresh.

The offending line can be changed to:

_this.scheduleAccessTokenRefresh(expiresAt, token);

Refresh Token

Hello,

I am wondering if this library could support a refresh token feature or if you guys have suggestions on how to implement that when using this package with django rest framework jwt?

I am thinking maybe have option to refresh token X seconds from last mouse movement?

Let me know thanks!

Readme example

Just curious why the readme switched to showing the non-mixin way to use the token/jwt authenticators? Trying to debug what has changed thats breaking my app after updating various packages including this one and noticed that the simple auth readme shows the mixin version (which I used previously and the readme used to include)

Again mainly asking bc trying to determine what has changed...

// app/controllers/login.js
import LoginControllerMixin from 'simple-auth/mixins/login-controller-mixin';

export default Ember.Controller.extend(LoginControllerMixin, {
  authenticator: 'authenticator:custom'
});

Dependency on Simple Auth

Why is the dependency on the Simple Auth library not specified in the package.json?
I just did npm install --save-dev ember-cli-simple-auth-token and I expected it to pull in the necessary libraries, but after running ember build I noticed it didn't :P

Extend login to either add custom header or add extra data to token

Hello,

Another question I have is how could I pass extra data to the backend when the auth/token request is sent? The use case is a User can optionally have multiple roles in the system and since the API is supposed to be stateless, I want to add the ability to send the chosen role type w/each request from the front to back in either the token (to be decoded by DRF) or as a header.

Any input on this would be greatly appreciated!

timeFactor default value and documentation

So, I started using this and was wondering why my tokens weren't being refreshed automatically.

Turns out it was because the timeFactor was set incorrectly. My server returned expiry times in seconds (Unix time).

It took me a while to track the problem because I assumed timeFactor to be correctly set to 1 because the docs say that the default value of 1 refers to seconds:

  /**
    Default time unit.
    @property timeFactor
    @type Integer
    @default 1 (seconds)
  */
  timeFactor: 1,

Then I saw the code comparing the expiry time to JS getTime() which is in milliseconds.

Since the JWT spec specifies that the time should be in seconds, shouldn't the default be 1000?

refreshLeeway

Hi there,

refreshLeeway is documented as an option, but it doesn't appear that this property appears anywhere in the library code.

Am I missing something?

When setting Token Prefix to empty default shows up

I don't have any token prefix. The only way I could make it work is to hard code the authorize code.

If I set authorizationPrefix to empty the default 'Bearer ' still shows up in the request.

 authorizationPrefix: '',

Return data record from authentication

I would like to return a data record from the authenticate request. Something like this:

{
   "token":"eyJhbGciOiJIUzI1NiIsIskdj........",
   "account":{
      "id":"account_1",
      "username":"Demo",
      "role":"CLIENT",
      "personKey":"client_1"
   }
}

Account should be deserialized to an account model record.

Is there any way to accomplish this in the current version of simple auth token?

buildToken doesn't return token

So this.get('session.secure.' + this.tokenPropertyName); returns undefined, but
this.get('session.content.' + this.tokenPropertyName); returns the token.

serverTokenEndPoint

Hi,
how can I change the serverTokenEndPoint in the environment.js

I use this configuration in

//config/environment.js
ENV['simple-auth'] = {
authorizer: 'simple-auth-authorizer:token'
};

ENV['simple-auth-token'] = {
serverTokenEndPoint : 'http://localhost:3000/login',
refreshAccessTokens: true,
timeFactor: 1,
refreshLeeway: 300
};

api-token-auth/ is instead always used !!

bildschirmfoto 2015-05-13 um 15 57 21

JWT automatic refresh, configurable interval?

The automatic refresh for JSON Web Tokens works, but it is cutting it kind of close by refreshing at the absolute last second that the previous token is still valid. Just a bit of lag could cause the old token to reach the API server in an expired state.

I would like to refresh the token a little while before the old one expires. Can this margin be made a configurable parameter?

Support for Ember Data requests

We're using Ember with Ember Data and all request from Ember Data do not containt the Authorization header.
Our research showed up two possible causes:

  • Ember Data overwrittes the header settings
  • Ember Data does not use jqXHR at all

Any ideas on this?

Token seems to be lost after refresh

Hi

I have all this running using tokens but when the page is refreshed the token is lost from the headers. I can see that it is still in session looking at the local storage.

Problem is that I had to manually add it in the 1st place by overriding the authenticate method on my login controller:

authenticate: function() {
            var _this = this;
            this._super()
                .then(function(cb1, cb2) {
                    var user = _this.get('session.user');
                    var token = _this.get('session.access_token') || _this.get("session.token");
                    if (_this.get('session.isAuthenticated') && !Ember.isEmpty(token)) {
                        Ember.$.ajaxSetup({
                            beforeSend: function(xhr) {
                                xhr.setRequestHeader('access_token', token);
                                                                xhr.setRequestHeader('token', token);
                            }
                        });
                    }

                    if (user.username === undefined || user.username == null) {
                        _this.transitionToRoute('preferences');
                    }
                });
        }

I'm guessing that this hack should be unneeded and I am doing something wrong elsewhere?

I can see that the restore method is called in the token authenticator but yeah - no headers....

Error: No authorizer was configured for Ember Simple Auth

I'm getting the following error on the console when starting up the App:

No authorizer was configured for Ember Simple Auth - specify one if backend requests need to be authorized.

My guess it that something has to be wrong with the configuration, so far I haven't find it. Here's what I have on my index.html (As shown on the docs):

  <body>
    <script>
      window.EmberENV = {{EMBER_ENV}};
    </script>
    <script src="assets/vendor.js"></script>
    <script src="assets/dashboard.js"></script>
    <script>
      window.DashboardNew = require('dashboard/app')['default'].create({{APP_CONFIG}});
    </script>
    <script>
      // https://github.com/jpadilla/ember-cli-simple-auth-token/issues/13
      window.ENV = window.ENV || {};
      window.ENV['simple-auth'] = {
        authorizer: 'authorizer:token',
        crossOriginWhitelist: ['http://localhost:3000']
      }
      window.ENV['simple-auth-token'] = {
        serverTokenEndpoint: 'http://localhost:3000/auth/sign-in'
      }
    </script>
  </body>

Any ideas?. Thanks!

Keep me logged in functionality

Currently you have a choice between having a long refresh interval on the tokens but having no way to update information on the token or you can have a short refresh interval but then the user has to login again frequently. I had an idea for a way to get the best of both:

On login with credentials instead of getting a single token you'd get 2, one would be the same as the current set up, it would have a short expiration but have an audience value that made it valid for making requests on the site but not valid for refreshing. The other token would have a much longer expiration but would have an audience that make it only valid for being used to refresh tokens to get new ones.

I.E. the short token could be valid for an hour and the long one for 2 weeks. But that means that if you deleted a user they would still be able to login for another hour but even though they have a valid long token they wouldn't be able to trade it in to get a new short token.

Thoughts?

buildToken(): session.[tokenPropertyName] or session.secure.[tokenPropertyName]?

It looks like buildToken() is looking for the token in session.[tokenPropertyName], which fails because the token is saved in session.secure.[tokenPropertyName]. This is preventing the token from being passed in the Authorization header.

Should buildToken() be looking in session.secure? Or is there a config error on my end?

Setting decoded user onto session

Hello @jpadilla ,

I'm now having two issues from the Ember side but it may relate to backend, I'm not sure. One is when I try to navigate to any url directly such as entering e.g. http://localhost:8000/users/1/ into the browser, I get logged out immediately and it says 401 Unauthorized.

EDIT: I think first issue is due to me having to explicitly define a Route.model in Ember. I thought it just used the default and I didn't have to define it but by defining it I can navigate to specific endpoints and it doesn't kick me out.

Second issue is relating to my previous issue I posted here which I had worked as of last week but seems to now break. That is my custom initializer that takes the decoded user id from the JWT token is failing at line:

https://github.com/erichonkanen/cl-frontend/blob/master/app/initializers/session.coffee#L25 (using custom jwt_payload_handler that passes 'user' back)

It tries to GET /api/v1/users/ and it seems that it's not authenticated yet so it results in 401 Unauthorized error. http://imgur.com/a/NjjCv

Is there a better way to do that or does that look wrong? This was working and I don't know what changed, I hadn't touched it in a week or so and came back and now it just doesn't work. My goal is to have the logged in user set on the session so I can do e.g. session.user.email. My repo above has latest code...

PS: I took the initializer code idea from below and what you said in my last issue post:

https://github.com/simplabs/ember-simple-auth/blob/master/examples/4-authenticated-account.html#L102

getting the authenticated user

Hey man I've been searching online but there's not a lot of resources. Is the any way to get the current authenticated user information? The specific example is in a route:

export default Ember.Route.extend({

    model: function(){

        return this.store.find('cart', "I want to pass in the current users id")

    }

});

any way for me to accomplish this?

'authenticate' not defined

After following instructions in readme I am getting this error:

Uncaught Error: Assertion Failed: TypeError: Cannot read property 'authenticate' of undefined

I've realised that the authenticate action is defined in the mixin, what I cannot understand is why it's not being called when my login form gets submitted? Could someone please help me establish what is causing the undefined error message?

Thanks,

Zac

Decode token data and expose

Something like this should work.

  // Get the part of the token where data is stored.
  token = token.split('.')[1];

  // Make URL friendly.
  token = token.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');

  // Reverse to original encoding.
  if (token.length % 4 !== 0) {
    token += ('===').slice(0, 4 - (token.length % 4));
  }

  token = token.replace(/-/g, '+').replace(/_/g, '/');

  // Return the token data decoded.
  return JSON.parse(atob(token));

Session lost on page refresh

Hello,

I'm noticing an issue where if I do a hard refresh of a page in my Ember app, the custom session appears to be lost. I'm not sure if this is something related to this package or not because the refresh does keep me logged in without redirecting to login page.

Error while processing route: organizations.index Cannot read property 'get' of undefined TypeError: Cannot read property 'get' of undefined

Here is my code where I create custom session, am I doing that correctly? I'm still new to Ember/Dependency injection etc...

https://github.com/erichonkanen/cl-frontend/blob/master/app/initializers/session.coffee

and here is my temp code for getting the session.user in a route:

https://github.com/erichonkanen/cl-frontend/blob/master/app/routes/organizations/index.coffee#L6

Also, where is the session stored? In my localstorage I notice "ember_simple_auth:token" which has just the token stored.. Should it be storing the custom session w/user object in it?

Any help appreciated!!

Thanks
(PS my goal is to be able to have access to the session.user from both templates (working) and routes (not working after refresh)

local storage getting cleared after refresh

Hey man this might be an issue with ember simple auth and not your token addon. I have a fresh install of both and a simple login set up it works fine but once I refresh it logs me out every time. I made a repo for the app to show you. https://github.com/threeaccents/enjoy_louville_app, I even created a new ember app to see if it was maybe anything I had done and just installed those 2 plugins and same error occurred.

Here is the resource generated by simple auth:

{"secure":{"authenticator":"simple-auth-authenticator:jwt","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJzdWIiOiIyIiwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwMDBcL2FwaVwvdjFcL2F1dGhlbnRpY2F0ZSIsImlhdCI6IjE0MzQyMTU2MjkiLCJleHAiOiIxNDM0MjE2MjI5IiwibmJmIjoiMTQzNDIxNTYyOSIsImp0aSI6IjBkOWQ4OTA2Y2YyNDQ2YmZkMDEyOTI5MzhmNzc4MjkyIn0.MzhjNzZkOGI2MDZiOWFlMzE2NzM2YTVlMDhhZjQ1ODMwZjVlYWZlOTcyMDhjZjAzNTRhNzA2MzY2MzQzYzM5Mw","expiresAt":"1434216229"}}

The issue was occurring because I had to change the timeFactor to 1000. There should be a check to see if the expires at length is the correct one.

I added a simple if statement:

if(expiresAt.length !== 13){ return reject(new Error('Invalid expiration')); }

I'm sure there could be a much better check. The other idea I had was instead of the user decideing the timeFactor there could be a simple function that counts the length of the exp and then just adds as many 0's as necessary for it to match the now variable.

The globPattern "undefined" did not match any files, so no file updates will be made.

I can't get this to work with the latest ember-cli 0.1.4. After installing and running the generator I get this:

$ ember generate simple-auth-token
version: 0.1.4
valid watchman found, version: [3.0.0]
installing
The globPattern "undefined" did not match any files, so no file updates will be made.
Installing browser packages via Bower...
  cached git://github.com/simplabs/ember-simple-auth-component.git#0.6.7
Installed browser packages via Bower.

So no real error there, but there is that warning which is suspicious...

Then when starting the server:

File: simple-auth-token/authenticators/token.js
ENOENT, no such file or directory '/Users/chris/personal/ember-jwt/tmp/tree_merger-tmp_dest_dir-KwihtSqx.tmp/simple-auth/authenticators/base.js'
Error: ENOENT, no such file or directory '/Users/chris/personal/ember-jwt/tmp/tree_merger-tmp_dest_dir-KwihtSqx.tmp/simple-auth/authenticators/base.js'
    at Object.fs.statSync (fs.js:695:18)
    at addModule (/Users/chris/personal/ember-jwt/node_modules/ember-cli/node_modules/broccoli-es6-concatenator/index.js:94:46)
    at addModule (/Users/chris/personal/ember-jwt/node_modules/ember-cli/node_modules/broccoli-es6-concatenator/index.js:143:9)
    at /Users/chris/personal/ember-jwt/node_modules/ember-cli/node_modules/broccoli-es6-concatenator/index.js:59:7
    at $$$internal$$tryCatch (/Users/chris/personal/ember-jwt/node_modules/ember-cli/node_modules/rsvp/dist/rsvp.js:470:16)
    at $$$internal$$invokeCallback (/Users/chris/personal/ember-jwt/node_modules/ember-cli/node_modules/rsvp/dist/rsvp.js:482:17)
    at $$$internal$$publish (/Users/chris/personal/ember-jwt/node_modules/ember-cli/node_modules/rsvp/dist/rsvp.js:453:11)
    at $$rsvp$asap$$flush (/Users/chris/personal/ember-jwt/node_modules/ember-cli/node_modules/rsvp/dist/rsvp.js:1531:9)
    at process._tickCallback (node.js:419:13)

Am I guessing correctly that the generator was supposed to add something to one of the core files? If so, what?

Note: this was a cleanly generated new ember-cli project, no other customization added.

Unnecessary data requests being made with token refresh request

I'm not exactly sure what could cause this but previously when a token refresh request went out, it was just that one request for a refreshed token.

Now when a token refresh request goes out, it additionally sends requests for data from all other endpoints used by that page.

Example: http://i.imgur.com/SynPpEH.png (all 3 of these requests are made together when it should only be the first one)

I thought maybe it's the demo app I'm using but I tried another demo app and same thing... If anyone has an idea let me know! It's not critical but just seems like it shouldn't be doing it

Cannot restore session after page refresh

It seems I cannot find a way to restore the session on page reload.

As soon as the session is authenticated, localStorage contains the correct data. Then if I refresh the page I get redirected to the login page and localStorage is empty.

Is this supposed to be the standard behavior?

Classes should be importable

I was trying to override the token authenticator to customize the getAuthenticateData function.
I generated an initializer using ember-cli.

I was expecting to import the token authenticator like this:
import TokenAuthenticator from 'simple-auth-token/authenticators/token';

But that didn't work (file not found). Instead I had to do:
import TokenAuthenticator from 'myapp/authenticators/token';

Any idea why this happens?

Demo

A live running demo/example of this would be great. It should help visualize what is going on.

Refreshing page in 0.7.1 leads to blank screen

Just dove back into this repo and upgraded from < 0.6.0 to 0.7.1 (with simple auth 0.8.0 beta2) and noticed that if I refresh the page, it leads to a blank screen and frozen app (until I delete the cookie).

I downgraded to 0.6.0 and the refresh functionality works so something possibly broke it or I am missing something? I will continue to look at this during the week!

-E

Opening the same app in multiple tabs - refresh gets confused

For some reason, if I open my app in two tabs simultaneously, ember-cli-simple-auth-token starts requesting refreshes at an insane rate (i.e. floods my development server).

Am I doing something wrong?

Scheduling access refresh for 1432182535
10jwt.js:213 Scheduling access refresh for 1432182536
10jwt.js:213 Scheduling access refresh for 1432182537
11jwt.js:213 Scheduling access refresh for 1432182538
10jwt.js:213 Scheduling access refresh for 1432182539
10jwt.js:213 Scheduling access refresh for 1432182540
11jwt.js:213 Scheduling access refresh for 1432182541
10jwt.js:213 Scheduling access refresh for 1432182542
8jwt.js:213 Scheduling access refresh for 1432182543
10jwt.js:213 Scheduling access refresh for 1432182544
10jwt.js:213 Scheduling access refresh for 1432182545
12jwt.js:213 Scheduling access refresh for 1432182546
8jwt.js:213 Scheduling access refresh for 1432182547
9jwt.js:213 Scheduling access refresh for 1432182548

Jwt doesn't work out of the box with ember-cli.

Using the latest version of ember-cli (0.1.15), the latest install steps do not seem to be working when using JWT (simple-auth-authenticator:jwt).

I ran the following steps in a clean dummy project:

npm install --save-dev ember-cli-simple-auth
ember generate ember-cli-simple-auth
npm install --save-dev ember-cli-simple-auth-token
ember generate simple-auth-token

Following this, I created the login route and controller for jwt authentication per the steps listed in the README.md.

Once I try to login, I get the following error in chrome:

Uncaught Error: Assertion Failed: No authenticator for factory "simple-auth-authenticator:jwt" could be found

Authorizer not setting Header

Hi I've tried to make the recent versions work. Seems like I can get the authentication header to be added to the requests.

Any ideas why it's not working?

here is my index.html

 window.ENV['simple-auth'] = {
    authorizer: 'authorizer:token'
  };
  window.ENV['simple-auth-token'] = {
    serverTokenEndpoint: 'http://localhost:1337/api/v1/login',
    authorizationPrefix: '',
    tokenPropertyName: 'token',
    authorizationHeaderName: 'access_token',
    identificationField: 'username'
  };

My route

import Ember from 'ember';
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';

export default Ember.Route.extend(AuthenticatedRouteMixin, {
model: function() {
    return this.store.find('testrestrictedmodel');
}
});

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.