aaugustin / django-sesame Goto Github PK
View Code? Open in Web Editor NEW"Magic Links" - URLs with authentication tokens for one-click login
Home Page: https://django-sesame.readthedocs.org/
License: BSD 3-Clause "New" or "Revised" License
"Magic Links" - URLs with authentication tokens for one-click login
Home Page: https://django-sesame.readthedocs.org/
License: BSD 3-Clause "New" or "Revised" License
Thanks for the package!
I'm trying to more securely create magic links. Say we want to send emails where users can approve a purchase. Right now we can generate a token
User = get_user_model()
user = User.objects.first()
magic_link = f"{HOSTNAME}/transaction/49/approve{get_query_string(user)}"
But if a motivated attacker wanted, they could submit another pending transaction, 59. And
GET HOSTNAME/transaction/59/approve?sesame=1234
If we could add the transaction id in the token, the api could unpack and apply the operation.
dict = get_parameters(user)
dict['transaction'] = 59
token = get_token(dict)
magic_link = f"{HOSTNAME}/transaction/approve?token={token}"
Is this already supported?
Hi, so I tried to install the package.
After I added the middleware, my envoriment didn't load, and got the following exception:
File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_bundle/pydev_monkey.py", line 589, in __call__
return self.original_func(*self.args, **self.kwargs)
File "/Users/yonicohen/Projects/Cleanly_1.11/venv/lib/python2.7/site-packages/django/utils/autoreload.py", line 227, in wrapper
fn(*args, **kwargs)
File "/Users/yonicohen/Projects/Cleanly_1.11/venv/lib/python2.7/site-packages/django/core/management/commands/runserver.py", line 147, in inner_run
handler = self.get_handler(*args, **options)
File "/Users/yonicohen/Projects/Cleanly_1.11/venv/lib/python2.7/site-packages/django/contrib/staticfiles/management/commands/runserver.py", line 27, in get_handler
handler = super(Command, self).get_handler(*args, **options)
File "/Users/yonicohen/Projects/Cleanly_1.11/venv/lib/python2.7/site-packages/django/core/management/commands/runserver.py", line 68, in get_handler
return get_internal_wsgi_application()
File "/Users/yonicohen/Projects/Cleanly_1.11/venv/lib/python2.7/site-packages/django/core/servers/basehttp.py", line 47, in get_internal_wsgi_application
return import_string(app_path)
File "/Users/yonicohen/Projects/Cleanly_1.11/venv/lib/python2.7/site-packages/django/utils/module_loading.py", line 20, in import_string
module = import_module(module_path)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/importlib/__init__.py", line 37, in import_module
__import__(name)
File "/Users/yonicohen/Projects/Cleanly_1.11/Cleanly/wsgi.py", line 10, in <module>
application = WSGIHandler()
File "/Users/yonicohen/Projects/Cleanly_1.11/venv/lib/python2.7/site-packages/django/core/handlers/wsgi.py", line 151, in __init__
self.load_middleware()
File "/Users/yonicohen/Projects/Cleanly_1.11/venv/lib/python2.7/site-packages/django/core/handlers/base.py", line 82, in load_middleware
mw_instance = middleware(handler)
TypeError: __init__() takes exactly 1 argument (2 given)
A user just reported it to us in: etesync/server#77
Essentially, URLs ending in are not processed correctly by Thunderbird and the last dash is not part of the link when clicking on it.
I bet it's not the only piece of software that fails this way. Is it possible to prevent that in sesame by e.g. always appending a character that's removed or just making sure the URLs never end with a - in some other way?
I'm going to add a workaround locally, which is appending &a=a
, though a nice solution would be preferred. Thanks!
I use a TextField pk, specifically https://github.com/ergeon/python-flax-id
It appears that last login field is not updated at all when magic link is used to auth.
Hi,
I wanted to implement scenario, where user gets access only to one view through the e-mail and is then able to set only few things on that view. I use django-sesame
for generating the key, and I have following code for the views:
class SesameUserMixin():
def get_object(self):
backend = ModelBackend()
self.token = self.request.GET['url_auth_token']
user = backend.parse_token(self.token)
if user is None:
raise PermissionDenied("bad token")
return user
def get_success_url(self):
return "%s?url_auth_token=%s" % (super().get_success_url(), self.token)
Do you think, that such thing should be part of django-sesame
and it's documentation? Should I make PR from this?
I am also considering if it is possible (and advisable) to sign the view name or url into the token and then. Then it would be possible to create tokens valid only for certain views (and possibly it's parameters).
Hi! I have my django set up with a custom user model, that uses UUID as user id, but when I try to use sesame, it fails with an exception:
Traceback (most recent call last):
File "/home/gamepad/.local/share/virtualenvs/backend-kGdHPQqo/src/python-telegram-bot/telegram/ext/dispatcher.py", line 301, in process_update
handler.handle_update(update, self)
File "/home/gamepad/.local/share/virtualenvs/backend-kGdHPQqo/src/python-telegram-bot/telegram/ext/conversationhandler.py", line 320, in handle_update
new_state = self.current_handler.handle_update(update, dispatcher)
File "/home/gamepad/.local/share/virtualenvs/backend-kGdHPQqo/src/python-telegram-bot/telegram/ext/regexhandler.py", line 173, in handle_update
return self.callback(dispatcher.bot, update, **optional_args)
File "/mnt/devenv/workspace/mydeposit/backend/tgbot/send_action.py", line 13, in command_func
return func(bot, update, **kwargs)
File "/mnt/devenv/workspace/mydeposit/backend/tgbot/auth.py", line 18, in command_func
return func(bot, update, socialaccount.user, **kwargs)
File "/mnt/devenv/workspace/mydeposit/backend/tgbot/main.py", line 263, in website_handler
telegram.InlineKeyboardButton(BTN_WEBSITE, url='https://127.0.0.1/' + sesame.utils.get_query_string(user))
File "/home/gamepad/.local/share/virtualenvs/backend-kGdHPQqo/lib/python3.6/site-packages/sesame/utils.py", line 21, in get_query_string
return '?' + urlencode(get_parameters(user))
File "/home/gamepad/.local/share/virtualenvs/backend-kGdHPQqo/lib/python3.6/site-packages/sesame/utils.py", line 13, in get_parameters
return {TOKEN_NAME: UrlAuthBackendMixin().create_token(user)}
File "/home/gamepad/.local/share/virtualenvs/backend-kGdHPQqo/lib/python3.6/site-packages/sesame/backends.py", line 67, in create_token
return self.sign(struct.pack(str('!i'), user.pk) + h)
struct.error: required argument is not an integer
Can you consider custom user id packing scheme?
Hello,
Would anyone have implemented django-sesame in a django project to enable the solution "login by mail" ?
Hi, is it possible to invalidate the token before its expiration ?
My use case is that a user fill a form (wizard) and at the end of the wizard, if the data are valid, they are stored in the db and the token must be invalidated.
Thanks.
It might be nice if there was an option to redirect to drop the token from the URL after successful authentication. Would this be possible?
Outlook Web Access uses a Bing crawler to perform some kind of preview of links for users. Unfortunately, this process also expires the django-sesame link before a user clicks on it. The crawling seems to take a few seconds, so sometimes a user can successfully use the link if they act quickly, but usually not.
I'm exploring ways to block the crawler from the site, but I thought I'd add this issue here for others experiencing reports of invalid one-time links.
It would be nice to have support for BigAutoField primary keys.
Would it be possible to just use the IntPacker
for this field or do we have to introduce a LongPacker
:
class LongPacker(object):
@staticmethod
def pack_pk(user_pk):
return struct.pack(str("!l"), user_pk)
@staticmethod
def unpack_pk(data):
return struct.unpack(str("!l"), data[:4])[0], data[4:]
Thanks so much for your great work on this package, I'm using it on a new project and it's been really seamless so far.
One thing I'm struggling with, is there a recommended way of handling the event in which tokens are either expired or one time tokens have already been used? I'd like to display an error message or redirect users to a view if they were unable to log in. This recent issue seems to touch on the subject: #41
Is there a standard Django way of handling failed authentication that has gone through all the authentication middleware or would I need to extend Sesame to handle this case?
Thanks again
I would like to implement a passwordless model that checks email ownership and registers a user in one go. The idea is to get to the login page. This creates an account for the user if that email is not registered, and sends a magic link for the user to login. Is this something you support? At no moment passwords are created.
I have had a look through the git README.md and existing issues and was not able to find anything related to it. I apologize if I missed it somewhere.
Best,
Miguel
I'm creating this issue for the sole purpose of noting why I'm wontfixing it.
A context processor would encourage adding tokens to URLs liberally and increase the risk to leak a token.
Tokens should be generated only when you intend to share a link through a (sufficiently) secure channel, like a private email or IM.
I see it supports custom user models, provided they have password field, what if this field NEVER has a value set? We completely disregard the password field in our current implementation but it still exists on our custom user model, will this result in duplicate tokens? I see it's specified that by default, sesame's tokens don't expire but are tied to the password of the user with changing the password invalidating the token, how would this impact our system if we were to switch from our custom passwordless system (simple short lived generated code sent via email and passed back via urlparam)?
Currently there are two uses of the SESAME_SALT
setting, which likely have different characteristics.
From a quick analysis:
sign
and unsign
is for preventing an arbitrary string signed somewhere else in the same app (i.e. with the same SECRET_KEY) to be accepted by sesame as a valid token. It doesn't need to be secret. It just needs to be unique within the app.create_token
and parse_token
is for making rainbow table attacks more difficult. (If such attacks are part of the threat model, probably you have bigger problems with django-sesame, but let's try to do things properly anyway.) Having the same salt for every token here makes no sens. Generating a random salt and storing it in the token would be better, but that would make tokens longer, while keeping them short is a design goal.Ideas:
It would be nice to tell in the documentation if the user must have a usable password or not. A note about considerating the is_active
flag would be nice too.
Idea: support a max_age parameter wherever a scope parameter is supported.
See #57 for the use case.
Hi,
I am trying to use sesame current latest 1.x django (1.11.27
). Sadly I still have to use python 2.x for this project.
Seems like latest version supporting python 2.x is 1.7
. It however isn't compatible with latest 1.x django due to get_response
parameter of middleware constructor not having default None
value:
https://docs.djangoproject.com/en/1.11/topics/http/middleware/#init-get-response
I suggest adding to documentation, that 1.7 is latest and backport this so that it's working with latest 1.x django. I don't mind making pull request if you are willing to take time checking and merging it. I am fine either way, can install from custom branch.
Thank you for the project!
I am getting the following error when a authenticate
is being called in another third party library and the authentication is not valid so it moves on to the next backend which is sesame and there is no url_auth_token.
TypeError in sesame.backends.ModelBackend, here's the traceback before Django swallows it:
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/sesame/backends.py", line 106, in authenticate
return self.parse_token(url_auth_token)
File "/usr/local/lib/python3.7/site-packages/sesame/backends.py", line 75, in parse_token
data = self.unsign(token)
File "/usr/local/lib/python3.7/site-packages/sesame/backends.py", line 52, in unsign
data = self.signer.unsign(token, max_age=self.max_age)
File "/usr/local/lib/python3.7/site-packages/django/core/signing.py", line 187, in unsign
result = super().unsign(value)
File "/usr/local/lib/python3.7/site-packages/django/core/signing.py", line 165, in unsign
if self.sep not in signed_value:
TypeError: argument of type 'NoneType' is not iterable
I would suggest handling if url_auth_token is None, then just returning like the following:
backends.py
def authenticate(self, request, url_auth_token=None):
"""
Check the token and return the corresponding user.
"""
# SEE THIS BLOCK HERE
if url_auth_token is None:
return
# End of block
try:
return self.parse_token(url_auth_token)
except TypeError:
backend = "%s.%s" % (self.__module__, self.__class__.__name__)
logger.exception(
"TypeError in %s, here's the traceback before Django swallows it:", backend
)
raise
Let me know what you think and if there is a reason for the way it is now. Thanks!
Does it make sense to set a cookie on the client when the user clicks "get link" and then check against the cookie before the authentication succeeds?
I saw that the version on PyPi is at 0.1, while in the repo it is already at 1.0. Could you update the version on PyPi?
When a user tries to authenticate his session with an invalid token, is there any way to understand why? I was thinking of along the lines of seeing if the cause is that the token has already been used (I have one-time-use tokens set to True), or if the token has expired, or being able to get some clue as to why it has failed.
I am not interested in doing this to show to the users, I am interested in it for debugging purposes.
Best,
Miguel
Suggested by Florian Apolloner:
If you are changing the algorithm, I'd probably switch over to blake2, especially because you can choose the size of your output with the
digest_size
argument.
While there's no version number in tokens, since the token format is <URL-safe Base64 encoded data>:<signature>
, it's possible to adding a version number in the payload, using a URL-safe character outside of the URL-safe Base64 alphabet as a delimiter. If this character is present in the payload, then it's the new version. If not, it's the old version. RFC 2396 says our options are "." | "!" | "~" | "*" | "'" | "(" | ")"
(since "-" | "_"
are already taken).
Per the Cryptographic Doom Principle, the version number should be in the payload, so it's included in the signature. Something like <URL-safe Base64 encoded data>:<version>:<signature>
would be a bad idea.
I'm seeing an issue where linked viewed in webmail clients are being truncated when clicked on - resulting in a login failure. This seems to be a recent change to webmail or browsers as it didn't happen last year. Copy/paste of the same link from the webmail message to the address bar work fine. I'd welcome any suggestions for working round this issue.
Tried 1.2, its works but need one_time feature
In signing.py, in class TimestampSigner, in function unsign, on the line 188, which is:
value, timestamp = result.rsplit(self.sep, 1)
There's fewer than 100 commits so it's still doable.
I appreciate your work @aaugustin. Wanted to discuss with you one thing.
This package can be useful for rest-framework too. For my purposes I have taken sesame.backends.UrlAuthBackendMixin
and mixed it with rest_framework.authentication.BaseAuthentication
.
This is what I had to change:
Authentication
rather than in Middleware
rest_framework.exceptions.AuthenticationFailed
in case token is wrongget_user
and user_can_authenticate
methods from django.contrib.auth.backends.ModelBackend
But I would propose to support DRF natively. I can prepare PR but maybe you have some vision of how this should be implemented in the best way?
Thank you.
The issue with Safari getting logged out by the redirect after clicking the magic link still occurs on iOS browsers including mobile Safari, Chrome, and Firefox. Making the browser check in is_safari
a bit more flexible fixes it locally, and I can put in a PR for that if it's useful
Hi Aymeric,
First of all, thank you very much for creating and maintaining django-sesame and making it freely available. Fantastic piece of code that exactly solves my problem.
I've been investigating the way tokens are generated and I'm wondering if it wouldn't be more secure to use only the password hash as input to the multiple-iteration MD5 function.
django-sesame/sesame/backends.py
Line 106 in f21003d
Currently get_revocation_key()
uses User.password
, which is of the form:
<algorithm>$<iterations>$<salt>$<hash>
Notice that the user's salt (a sensitive piece of data) is included there, unencrypted. Everything hinges on the security of PBKDF2/MD5. If that algorithm is ever reversed (I realize this is not very likely), an attacker could obtain both the salt and the hashed password. This would then make it fairly easy to dictionary-attack the password.
Now if get_revocation_key()
only used the <hash> part, reversing PBKDF2/MD5 would only give you the password hash. And, of course, if you only know the password hash, but not the salt, the password is much much much harder to crack (10^20 times more work!). (In fact, it seems if you only used the hash part, you could get away without MD5 -- just include the hashed password in the token. The extra MD5-ing wouldn't make much of a difference compared to the extra 10^20 factor.)
Resistance to dictionary attacks would appear to be the same (assuming the attacker has an expired token and wants to guess the password), because the password hash is based on the password and the salt.
Of course, I know I can change MD5 to something else. But it seems so easy to eliminate an (admittedly theoretical) vulnerability. A salt is sensitive data and here it seems unnecessary to include it in the token.
Please don't hesitate to let me know if I'm missing something. I'm not a security expert, so it's quite possible (even likely) I screwed up somewhere :)
Hey,
I don't know if it's on the roadmap or even if it's of any interest here, but I'd love to see a way to add a scope to sesame tokens. What do I mean? Designate them to specific views, or specific operations.
For example, you can generate tokens that are used for email login, but at the same time, also generate tokens that can only be used for one-time view of screens. Tokens use for email verification (e.g. for verifying a user approves of an account deletion).
I ended up reimplementing sesame in my project in order to support this, though I wish it was supported here.
Feel free to close this if you think it's out of scope for this project, and thanks a lot for creating it!
Hey guys.
Loving this project. Using it on Django 2.0.
Just a quick question - is there a way I can redirect to a specific page after login with the token generated by sesame? Such as /profile /settings etc
Thanks!
Hi,
We are using django-sesame and sending the OTP over email, so we hit the problem with SESAME_ONE_TIME=True
and the email server checking the links. As per the documentation we will change to SESAME_ONE_TIME=False
, and a short MAX_AGE
.
Would it be possible to have a configuration option to include the user's IP
in the signature? authenticate
could pass the request
or the IP
to parse_token
, and in the same way we would be passing the request
or IP
to create_token
.
This, along with a well configured webserver/proxy, would allow us to use SESAME_ONE_TIME=True
again, plus a bigger MAX_AGE
which is IMHO better for users.
Thanks in advance!
When sesame is passed malformed tokens the middleware just throws an exception. This can happen, for example, when the user copies the URL incorrectly or if the URL isn't processed correctly by some user app (such as #63).
For example, if you try this:
http://localhost/dashboard/?sesame=oeuh3
You'll get Invalid base64-encoded string: number of data characters (5) cannot be 1 more than a multiple of 4
as the error.
Expected behaviour: treat it as a bad token rather than crashing.
I am migrating an existing project from Django 3.1.8 to Django 3.2.
Sesame stopped working, the error is in tokens_v2.py, line 129, in create_token
primary_key = packers.packer.pack_pk(user.pk)
I think it might be related to this change, which affect also user model:
https://docs.djangoproject.com/en/3.2/releases/3.2/#customizing-type-of-auto-created-primary-keys
Is it possible to add Django 3.2 to the tests?
Hi @aaugustin I am a logo designer. I have reviewed and appreciate your depo. I want to support the open source community by designing logos. I designed a logo for django-sesame. I want to know what you think. Best regards.
Readme file view;
I have several authentication backend configured in Django.
I use an url_auth_token in a URL to allow Insomnia to import from this URL that needs authentication.
When I call the URL with a token that has expired, the sesame middleware return None, letting other middleware to kick in instead of raising a PermissionDenied.
This is a problem (for me) as the following authentication backends redirect the users to login pages (which Insomnia does not handle properly).
Debugging this is difficult and it would be better to see a 403 (giving the user an hint that the token is not valid.
In utils.py/get_user method, I would adapt the lines 42-44 to
user = authenticate(request, url_auth_token=url_auth_token)
if user is None:
# return None
from django.core.exceptions import PermissionDenied
raise PermissionDenied
does it make sense ?
We could do a salted_hmac of user id + password hash but only add the user id to the token.
(suggested by @apollo13)
Cf. #20 (comment)
My use case is to use Sesame as a form of 2FA. Eg user enters username + password then emails the Sesame auth link to login.
Just to clarify, I've set the following in my settings.py, so that users can change their password at in settings.
SESAME_INVALIDATE_ON_PASSWORD_CHANGE = False
However users are still being logged out after changing their password as it redirects to /accounts/login upon refresh.
I've set up a test for token re-use with SESAME_ONE_TIME
set to True
and I'm running the following test:
def test_reuse(self):
alice = User.objects.create_user(username='[email protected]', password='foobar123')
token = get_parameters(alice)['url_auth_token']
assert token
user = authenticate(url_auth_token=token)
assert user == alice
user = authenticate(url_auth_token=token)
assert user is None # fails: user still alice
The issue appears to be that user.last_login
remains None
after authenticate
is being called and therefore in backends.py:97
if self.one_time:
value += str(user.last_login)
does not lead to a change in get_revocation_key
.
I wonder: is my assumption wrong that authenticate
should count as a login in therefore invalidate the token? Or should I be using a different mechanism to use and invalidate the token?
Thanks a lot!
I wasn't able to integrate this library into my django application after following the Getting Started section. This is what I get when after following the installation procedure:
Traceback (most recent call last):
web_1 | File "/usr/local/lib/python3.7/threading.py", line 917, in _bootstrap_inner
web_1 | self.run()
web_1 | File "/usr/local/lib/python3.7/threading.py", line 865, in run
web_1 | self._target(*self._args, **self._kwargs)
web_1 | File "/root/.local/share/virtualenvs/code-_Py8Si6I/lib/python3.7/site-packages/django/utils/autoreload.py", line 54, in wrapper
web_1 | fn(*args, **kwargs)
web_1 | File "/root/.local/share/virtualenvs/code-_Py8Si6I/lib/python3.7/site-packages/django/core/management/commands/runserver.py", line 137, in inner_run
web_1 | handler = self.get_handler(*args, **options)
web_1 | File "/root/.local/share/virtualenvs/code-_Py8Si6I/lib/python3.7/site-packages/django/contrib/staticfiles/management/commands/runserver.py", line 27, in get_handler
web_1 | handler = super().get_handler(*args, **options)
web_1 | File "/root/.local/share/virtualenvs/code-_Py8Si6I/lib/python3.7/site-packages/django/core/management/commands/runserver.py", line 64, in get_handler
web_1 | return get_internal_wsgi_application()
web_1 | File "/root/.local/share/virtualenvs/code-_Py8Si6I/lib/python3.7/site-packages/django/core/servers/basehttp.py", line 45, in get_internal_wsgi_application
web_1 | return import_string(app_path)
web_1 | File "/root/.local/share/virtualenvs/code-_Py8Si6I/lib/python3.7/site-packages/django/utils/module_loading.py", line 17, in import_string
web_1 | module = import_module(module_path)
web_1 | File "/root/.local/share/virtualenvs/code-_Py8Si6I/lib/python3.7/importlib/__init__.py", line 127, in import_module
web_1 | return _bootstrap._gcd_import(name[level:], package, level)
web_1 | File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
web_1 | File "<frozen importlib._bootstrap>", line 983, in _find_and_load
web_1 | File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
web_1 | File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
web_1 | File "<frozen importlib._bootstrap_external>", line 728, in exec_module
web_1 | File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
web_1 | File "/code/fqpc_api/wsgi.py", line 16, in <module>
web_1 | application = get_wsgi_application()
web_1 | File "/root/.local/share/virtualenvs/code-_Py8Si6I/lib/python3.7/site-packages/django/core/wsgi.py", line 13, in get_wsgi_application
web_1 | return WSGIHandler()
web_1 | File "/root/.local/share/virtualenvs/code-_Py8Si6I/lib/python3.7/site-packages/django/core/handlers/wsgi.py", line 135, in __init__
web_1 | self.load_middleware()
web_1 | File "/root/.local/share/virtualenvs/code-_Py8Si6I/lib/python3.7/site-packages/django/core/handlers/base.py", line 37, in load_middleware
web_1 | mw_instance = middleware(handler)
web_1 | TypeError: AuthenticationMiddleware() takes no arguments
Does anyone came across this issue? I think this is a bug.
Im using the following stack:
Hey!
I am using JWT auth using the dj_rest_auth
package and was wondering how that would work in conjuncture with Sesame. I can I login a user with a token in the url (which sesame provides) then login a user and return the jwt token?
Feature requested on #django-social:
2014-09-09 10:31:17 <jax> mYk: you know of anything like sesame but with expiring login tokens?
2014-09-09 10:32:52 <mYk> jax: I think you just need to subclass UrlAuthBackendMixin and change the Signer to use a TimestampSigner
I just had a big headache finding the root cause of an invalid url_auth_token. The problem is that the url_auth_token is created, among other things, using the current user password.
The SESAME_MAX_AGE when set to None, it allows us to create "eternal" tokens, I think it would be great that the url_auth_token does not depend on the password so those tokens generated are absolutely eternal.
Is it possible to add an option to make the tokens only usable once?
Hey, using your lib for a long time already. Great job!
I'm having two different scenarios for two user groups to use django_sesame
access pattern. But the MAX_AGE
should be different for them (10m vs. 24h). Can we make this value more flexible? E.g. can we allow SESAME_MAX_AGE
to be a callback getting request
and User()
objects?
Many thx in advance!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.