go-oauth2 / oauth2 Goto Github PK
View Code? Open in Web Editor NEWOAuth 2.0 server library for the Go programming language.
Home Page: https://pkg.go.dev/github.com/go-oauth2/oauth2/v4
License: MIT License
OAuth 2.0 server library for the Go programming language.
Home Page: https://pkg.go.dev/github.com/go-oauth2/oauth2/v4
License: MIT License
On param oauth in redirect uri can be duplicate with same grant type or others
e.g : http://localhost:9096/authorize?client_id=222222&redirect_uri=http%3A%2F%2Flocalhost%3A9088%2Foauth2&response_type=code&response_type=abc&scope=all&state=xyz
will be same result as
http://localhost:9096/authorize?client_id=222222&redirect_uri=http%3A%2F%2Flocalhost%3A9088%2Foauth2&response_type=code&scope=all&state=xyz
I'm trying to allow multiple client for single auth server, so implemented as:
...
firstClientStore := store.NewClientStore()
firstClientStore.Set("first-client", &models.Client{
ID: "first-client",
Secret: "first-client-secret",
Domain: "http://localhost:3333",
})
secondClientStore := store.NewClientStore()
secondClientStore.Set("second-client", &models.Client{
ID: "second-client",
Secret: "second-client-secret",
Domain: "http://localhost:4444",
})
manager.MapClientStorage(firstClientStore)
manager.MapClientStorage(secondClientStore)
srv := server.NewServer(server.NewConfig(), manager)
...
...but this seems not working well. It refuses the request.
How can I make it?
How can I use this library with the new Go Modules system?
First of all, thank you for your work.
Could you give me an example, or explain how I relate the user created in the database with the generated token?
In other words, how do I get the id of the user according to the generated token
If all the tokens/secrets are stored in plaintext, then in case of database compromise for read-only (typical for many types of injection bugs), it will be possible to escalate the issue to authentication bypass.
It might be desirable to allow the use of hash or encryption functions prior to token storage in the database to be used.
Ref:
https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Password_Storage_Cheat_Sheet.md#hash-the-password-as-one-of-several-steps
https://www.reddit.com/r/learnprogramming/comments/7ye9iw/how_should_oauth2_refresh_tokens_be_stored_in_the/dufvizt/?utm_source=share&utm_medium=web2x
https://stackoverflow.com/a/43009934
thephpleague/oauth2-server#387
https://tools.ietf.org/html/rfc6819#section-5.1.4.1.3
Hello
How do you think about implementing function which offers id_token, in OpenID-connect specification(https://auth0.com/docs/tokens/id-token)?
I know this could not be fit to this project, because this is out of spec in OAuth2. But when I was implementing authentication server based on this module, I felt this could be useful.
Thanks.
README.md says that the store can implement Redis. Is this for both client and token storage? Does the Redis package https://github.com/go-oauth2/redis do both? If so how do you use it with clients? The usage only shows an example for tokens.
Many of the fields of server.Server are callbacks that don't take either an *http.Request
or a context.Context
as arguments. This means it is impossible to use any request-specific information in them: For example, I'd like to log errors with a request ID, increment metrics with request-specific labels (like the path and method) and set a custom verbosity per request.
It would be great if there was a way to set these fields on a per-request basis. The simplest way would be to add a context.Context
field, but that would be backwards-incompatible. Another way would be to add a (for example) CallbacksForRequest(*http.Request) Server
function that could be used to return closures encapsulating all the request-specific data needed. That would be less ergonomical, but work.
Hi Team, I have created a basic setup for Oauth2(Password grant) using this library. Its working, but I need to add more custom claims. Currently only userid is added as below.
"UserID": "[email protected]",
"Role": "",
"Country": ""
}
My Question is do I need to make multiple DB queries if I needed to add these claims. Also I am not able to understand the entrypoint to this method. The last call I see is to Manager.GenerateAccessToken(gt, tgr)
Token(data *oauth2.GenerateBasic, isGenRefresh bool)
Thanks in advance
Hello~
I'm currently trying to implement auth server with this, and testing with example in repo.
I could understand the flow of how client side could receive token from server. After I received, how could I use this token to re-access client service without login, and get user information from auth server?
And in example, how could I change token expire duration?
Thanks.
Running through the auth flow, I'm getting
2018/03/29 01:10:56 http: panic serving 127.0.0.1:39968: interface conversion: interface {} is nil, not url.Values
goroutine 23 [running]:
net/http.(*conn).serve.func1(0xc420192140)
/usr/lib/go-1.10/src/net/http/server.go:1726 +0xd0
panic(0x744500, 0xc4202587c0)
/usr/lib/go-1.10/src/runtime/panic.go:505 +0x229
main.authHandler(0x7ed260, 0xc4201b01c0, 0xc4201a2800)
/home/count/src/github.com/go-oauth2/oauth2/example/server/server.go:119 +0x417
net/http.HandlerFunc.ServeHTTP(0x7bf450, 0x7ed260, 0xc4201b01c0, 0xc4201a2800)
/usr/lib/go-1.10/src/net/http/server.go:1947 +0x44
net/http.(*ServeMux).ServeHTTP(0x996e40, 0x7ed260, 0xc4201b01c0, 0xc4201a2800)
/usr/lib/go-1.10/src/net/http/server.go:2337 +0x130
net/http.serverHandler.ServeHTTP(0xc4201261a0, 0x7ed260, 0xc4201b01c0, 0xc4201a2800)
/usr/lib/go-1.10/src/net/http/server.go:2694 +0xbc
net/http.(*conn).serve(0xc420192140, 0x7ed4e0, 0xc4202586c0)
/usr/lib/go-1.10/src/net/http/server.go:1830 +0x651
created by net/http.(*Server).Serve
/usr/lib/go-1.10/src/net/http/server.go:2795 +0x27b
with the given example :(
https://tools.ietf.org/html/rfc6749#section-4.1.1
States that the redirect_uri is OPTIONAL
The server returns an error when redirect_uri
is omitted. https://github.com/go-oauth2/oauth2/blob/master/server/server.go#L152
redirectURI := r.FormValue("redirect_uri")
clientID := r.FormValue("client_id")
if !(r.Method == "GET" || r.Method == "POST") ||
clientID == "" ||
redirectURI == "" {
err = errors.ErrInvalidRequest
return
}
Could affect multiple authorization and token grants.
It looks like JWTs are somewhat-mishandled by the library.
After generating the JWT, it is stored exactly as-is in the database, and then checked by loading them from the database. This limits their use. Consider the following scenario:
Server A and Server B both issue tokens. Server B (our server) wants to accept both tokens issued by Server A and itself. The admin might want to use JWTs to resolve that. Server A issues the token, but then our server won't be able to accept it, even though it will be a valid JWT.
That aside, one of the biggest upsides of JWT is simplified token storage - the server now can be relieved of this burden, which is, again, limited in this case.
The correct behavior should be storing a token id inside the token itself, and then allowing a blacklist based on token ids.
One possible approach (Auth0): https://auth0.com/blog/blacklist-json-web-token-api-keys/
Hi,
It looks the generator only support 3 claims (Audience, Subject and ExpiresAt).
Does it support custmized claims? Or is there a plan?
Thanks
I am trying to implement all the OAuth2 authentication methods under the same program, but I am having an issue by just adding the Client Credential
method to the example/server.go
. When adding these two lines from your example in the readme
to the example/server.go
, the Authorization Code
flow doesn't work anymore.
srv.SetAllowGetAccessRequest(true)
srv.SetClientInfoHandler(server.ClientFormHandler)
And if I remove the 2 lines Authorization Code
is working again but Client Credentials
fails of course.
srv.SetUserAuthorizationHandler(userAuthorizeHandler)
//srv.SetAllowGetAccessRequest(true)
//srv.SetClientInfoHandler(server.ClientFormHandler)
So, either the Client Credential
flow works, either the Authorization Code
flow.
Can you advise on what is going on?
I think it would be great if you had a working server example with all the authentication method working together, this would make this open source oauth2 package far ahead from the other ones.
Thank you!
access_token
.access_token
.A use case is a client that's merely logging in a user and is happy to have the session last as long as an access_token
, without a persistent refresh_token
.
Right now, as far as I understand, it's "all authorization code swaps get a refresh token or none of them do". If I am mistaken, feel free to close this issue.
This issue has been opened automaticaly because we think you are using satori/uuid in your code. Unfortunately, satori/uuid has a critical defect where it doesn't generate random UUIDV4s satori/go.uuid#73 and should be replaced as the maintainer has been unresponsive for 5 months with the bug still open. Alternative packages you should consider: https://github.com/google/uuid https://github.com/pborman/uuid
When i try to build server from example it failed with this errors:
$ go build server.go
# gopkg.in/oauth2.v3/generates
../../../gopkg.in/oauth2.v3/generates/access.go:28:66: multiple-value uuid.NewV4() in single-value context
../../../gopkg.in/oauth2.v3/generates/access.go:31:68: multiple-value uuid.NewV4() in single-value context
../../../gopkg.in/oauth2.v3/generates/authorize.go:24:32: multiple-value uuid.NewV1() in single-value context
# gopkg.in/oauth2.v3/store
../../../gopkg.in/oauth2.v3/store/token.go:46:24: multiple-value uuid.NewV4() in single-value context
Golang version:
$ go version
go version go1.9.2 darwin/amd64
Same problem with compilation on Linux/Centos 7:
I noticed that you revoked the support for fasthttp. I'm curious what the reasoning behind that was and if there are any plans to include it in a future release?
Most of my go servers use fasthttp, and it kind of sucks having a unique snowflake that's using net/http.
Is there a simple way to transform a fasthttp-flavored handler into a net/http handler? I saw an adapter out there but that adapts the wrong way (it goes FROM net/http TO fasthttp not the reverse).
This isn't something I want to spend a lot of intellectual capital on, I'm just curious as it would be nice to have.
I got the response blow when I create the token by wrong username or password:
http code: 500
{
"error": "server_error",
"error_description": "The authorization server encountered an unexpected condition that prevented it from fulfilling the request"
}
How can I change the response content and http status code?
I'm trying to build an OAuth2 server. My access token (jwt type) server give me doesn't have iss (issuer) field. How can I add it to my token and another fields in payload? This is my server config and token server give me
https://i.stack.imgur.com/yDEy2.png
manager = manage.NewDefaultManager()
cfg := &manage.Config{
// access token expiration time
AccessTokenExp: time.Hour * 2,
// refresh token expiration time
RefreshTokenExp: time.Hour * 24 * 3,
// whether to generate the refreshing token
IsGenerateRefresh: true,
}
manager.SetAuthorizeCodeTokenCfg(cfg)
manager.SetAuthorizeCodeExp(time.Minute * 30)
manager.MapAuthorizeGenerate(generates.NewAuthorizeGenerate())
// generate jwt access token
manager.MapAccessGenerate(generates.NewJWTAccessGenerate([]byte("12345678"), jwt.SigningMethodHS256))
manager.MustTokenStorage(store.NewMemoryTokenStore())
clientStore = store.NewClientStore()
manager.MapClientStorage(clientStore)
srv = server.NewDefaultServer(manager)
srv.SetAllowGetAccessRequest(true)
// allow the grant types model:AuthorizationCode,PasswordCredentials,ClientCredentials,Refreshing
srv.SetAllowedGrantType("authorization_code")
// get client id and secrect, this is default handler
srv.SetClientInfoHandler(server.ClientFormHandler)
// way to get user ID
srv.SetUserAuthorizationHandler(userAuthorizeHandler)
//
srv.SetExtensionFieldsHandler(extensionFieldsHanler)
manager.SetRefreshTokenCfg(manage.DefaultRefreshTokenCfg)
srv.SetInternalErrorHandler(func(err error) (re *errors.Response) {
log.Println("Internal Error:", err.Error())
return
})
srv.SetResponseErrorHandler(func(re *errors.Response) {
log.Println("Response Error:", re.Error.Error())
})
I tried to find is api docs but I found nothing i tried config here https://go-oauth2.github.io/ but looklike "manager.MapTokenModel(models.NewToken())" doesn't work anymore, I dont know if it can help me
So I need some way to add more filed to my token. Thank for your help
There are at least 2 distinct cases - when we did not configure the user authorization handler, and when we did.
For the first case, if we use the server from samples (with domain set to http://localhost for the client), we can do the following:
$ curl 'localhost:9096/authorize?client_id=000000&response_type=code&scope=read&redirect_uri=http://test/' -v
....
< HTTP/1.1 302 Found
< Location: http://test/?error=access_denied&error_description=The+resource+owner+or+authorization+server+denied+the+request
....
For the second case, for the invalid url, we still get a redirect:
$ curl 'localhost:9096/authorize?client_id=000000&response_type=code&scope=read&redirect_uri=http://test/' -v
...
< HTTP/1.1 302 Found
< Location: http://test/?error=server_error&error_description=The+authorization+server+encountered+an+unexpected+condition+that+prevented+it+from+fulfilling+the+request
....
Also, by default, the domain is suffix-matched, meaning testlocalhost will be considered a valid domain:
$ curl 'localhost:9096/authorize?client_id=000000&response_type=code&scope=read&redirect_uri=http://testlocalhost/' -v
....
< HTTP/1.1 302 Found
< Location: http://testlocalhost/?code=M4-KSICRPGUZFL9EP7A1-A
...
All are instances of Unvalidated Redirects:
https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md
At least, those might open the app up to phishing attacks.
Also, according to OAuth2, redirect uri should be strictly whitelisted, as attacker can use that to leak codes: https://www.oauth.com/oauth2-servers/redirect-uris/redirect-uri-validation/
how to set time expires_in
Can I remove a client created in NewClientStore function?
Internally, token generation uses UUIDv3 and UUIDv5. RFC4122 recommends against using them as security credentials.
Ref: https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based).
Additionally, after generation they are base64-encoded and converted to uppercase, reducing entropy a bit more. Generically, according to https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Session_Management_Cheat_Sheet.md#session-id-entropy, the token will have entropy below the recommended minimum.
How to compile locally so that the code can be customized?
go build ./...
oauth2/example/server/server.go:11:2: cannot find package "oauth2/errors" in any of:
/usr/local/Cellar/go/1.10.1/libexec/src/oauth2/errors (from $GOROOT)
/Users/z001dv3/go/src/oauth2/errors (from $GOPATH)
oauth2/example/server/server.go:12:2: cannot find package "oauth2/manage" in any of:
/usr/local/Cellar/go/1.10.1/libexec/src/oauth2/manage (from $GOROOT)
/Users/z001dv3/go/src/oauth2/manage (from $GOPATH)
oauth2/example/server/server.go:13:2: cannot find package "oauth2/models" in any of:
/usr/local/Cellar/go/1.10.1/libexec/src/oauth2/models (from $GOROOT)
/Users/z001dv3/go/src/oauth2/models (from $GOPATH)
oauth2/example/server/server.go:14:2: cannot find package "oauth2/server" in any of:
/usr/local/Cellar/go/1.10.1/libexec/src/oauth2/server (from $GOROOT)
/Users/z001dv3/go/src/oauth2/server (from $GOPATH)
oauth2/example/server/server.go:15:2: cannot find package "oauth2/store" in any of:
/usr/local/Cellar/go/1.10.1/libexec/src/oauth2/store (from $GOROOT)
/Users/z001dv3/go/src/oauth2/store (from $GOPATH)
oauth2/generates/access.go:9:2: cannot find package "oauth2" in any of:
/usr/local/Cellar/go/1.10.1/libexec/src/oauth2 (from $GOROOT)
/Users/z001dv3/go/src/oauth2 (from $GOPATH)
oauth2/generates/access.go:10:2: cannot find package "oauth2/utils/uuid" in any of:
/usr/local/Cellar/go/1.10.1/libexec/src/oauth2/utils/uuid (from $GOROOT)
/Users/z001dv3/go/src/oauth2/utils/uuid (from $GOPATH)
oauth2/manage/manager.go:8:2: cannot find package "oauth2/generates" in any of:
/usr/local/Cellar/go/1.10.1/libexec/src/oauth2/generates (from $GOROOT)
/Users/z001dv3/go/src/oauth2/generates (from $GOPATH)
So I'm getting
gopkg.in/oauth2.v3/generates/access.go:28:66: multiple-value uuid.NewV4() in single-value context
gopkg.in/oauth2.v3/generates/access.go:31:68: multiple-value uuid.NewV4() in single-value context
gopkg.in/oauth2.v3/generates/authorize.go:24:32: multiple-value uuid.NewV1() in single-value gopkg.in/oauth2.v3/store/token.go:46:24: multiple-value uuid.NewV4() in single-value context
This seems to be fixed in the library, but a bit of investigation has revealed that gopkg.in hasn't been updated . Without putting any effort into researching the issue it seems to be because you have not tagged the fixed code with a version.
Can you please do this?
For example, when we use a refresh token, it gets regenerated along with access token. At this point both access and refresh token are no longer present (no references to base data are left), but it is still retained for the next 7 days (default TTL).
This is true at least for redis store.
I'm wondering why the ValidationAuthorizeRequest in server.go requires the /authorize endpoint to be a GET? We are seeing that many clients require the call to be a POST. Particularly since the form/querystring includes the client_id and client_secret. Why not allow GET/POST
(let me know if a pull request will help.)
// ValidationAuthorizeRequest the authorization request validation
func (s *Server) ValidationAuthorizeRequest(r *http.Request) (req *AuthorizeRequest, err error) {
redirectURI, err := url.QueryUnescape(r.FormValue("redirect_uri"))
if err != nil {
return
}
clientID := r.FormValue("client_id")
if r.Method != "GET" ||
clientID == "" ||
redirectURI == "" {
err = errors.ErrInvalidRequest
return
}
Hello,
Thank you for all your hard work!
Is there any way to use the scope to provide role-based authorization?
It seems that it's not possible today and scope is only used as a request by the user because models/client.go does not implement the scope.
Maybe it could have in models/client.go a list of authorized scopes for a client?
Thanks!
Running the example project I can login, authorize the app, and get redirected back to the client handler.
If I then try to access the root URL of the client, I have to login again. How can I use the token to ensure I don't have to login, until it is invalidated, or expired ?
Should the client persist the token in a cache, and then check that it is sent in the Authorization header. If present and not expired give access to the resource, otherwise call the code to perform auth again?
u := config.AuthCodeURL("xyz")
http.Redirect(w, r, u, http.StatusFound)
Whenever i restart the sever do i need to set clientstore again.
What if i have more users? @LyricTian
Trying to figure out the easiest way to store the access token and refresh token in an https only cookie. Have any guidance?
有个疑问,使用密码模式时,使用access token也可以刷新授权码,这样正常吗?
If we do a request with non-existing client_id in the sample server, we get the following response:
$ curl 'http://localhost:9096/token?grant_type=client_credentials&client_id=000000000&client_secret=11111111&scope=all'
{"error":"server_error","error_description":"The authorization server encountered an unexpected condition that prevented it from fulfilling the request"}
On the other hand, if we do a request with existing client_id, but invalid client_secret, we get the following response:
$ curl 'http://localhost:9096/token?grant_type=client_credentials&client_id=000000&client_secret=111111&scope=all'
{"error":"invalid_client","error_description":"Client authentication failed"}
This can be used to enumerate valid client_id's, which might be undesirable.
Hello,
We've been testing this library for a future project but there are a few things that we'd need to add:
0
duration that means the tokens never expire.Would you have any objection to us creating a PR to implement these? The 0
duration seems relatively straightforward, for the per-token expiration, I was thinking we'd add support for configuring a function much like SetInternalErrorHandler
that would be called on token minting and would return a Duration.
What do you think?
Thanks
So I'm trying to add a /register and /login endpoint that accepts a JSON payload with the email/password credentials of the user, and looking at the docs and the API, it's not readily apparent exactly what I should do to authenticate the user and issue a token.
The example is awesome for showing how to be an oauth server, but I'd like my local login flow to issue the token as well as any outside auth providers via oauth.
I'm certain that the library is capable of this, I'm just kind of struggling for the implementation.
func (client *localAuthClient) AuthenticateUser(email string, password string) (string, error) {
c := client.DB.C("users")
user := &models.User{}
err := c.Find(bson.M{ "email": email }).One(user)
if err != nil {
return "", err
}
if comparePasswords(user.Password, []byte(password)) {
return user.ID.Hex(), nil
}
return "", errors.New("email/password combination is not a match")
}
And in my main.go:
srv.SetPasswordAuthorizationHandler(localAuthClient.AuthenticateUser)
But I'm having a disconnect at this point. How do I instruct the API to run my password authentication flow in an endpoint that will generate a token (a JWT specifically, but that looks straightforward enough)?
Hi, 这个项目很棒,多谢你的贡献。
我现在有一个问题是,我想把DefaultManager的AccessGenerate替换成生成jwt的。而现在这部分的接口定义了只有GenerateBasic这个数据能参与token的生成,如果我想用到别的数据例如scope来参与生成jwt,这里没办法做到。
https://github.com/go-oauth2/oauth2/blob/master/generate.go#L20
我想问一下,这有什么好的解决方法吗?或者代码中哪里有这方面的拓展?如果没有,后续是否考虑加入呢?
谢谢
Hello.
When access token is expired and refresh token is still okay, how can I refresh access token with refresh token?
Thanks.
How do I customize this field?
Hi. I'm search oauth2 server package and find you repo and github.com/RangelReale/osin, can you provide some compare to github.com/RangelReale/osin package?
To help other users to decide
Currently the server requires that the client sends back its id and secret to the server when exchanging an authorization code for an access token:
But the OAuth2 RFC does not require this: https://tools.ietf.org/html/rfc6749#section-4.1.3 i.e. the code itself should be enough to uniquely identify the client (passing the client_id
parameter is allowed, though optional).
This means that currently this server implementation does not correctly work with the standard Go oauth2 library, and I guess many others.
I think a fix could be to maintain a map from authorization codes to clients, and use it to look up the client instead of expecting id and secret to be provided in the request.
我设置了mysql存储后发现存进去的数据如果过期了不会删除,这样是不是会导致使用时间越长检索token是否有效的时间也会越来越长(因为数据越来越多却不会删除掉)?能不能在后期增加自动删除过期token的功能呢?谢谢
2017/07/19 13:46:42 Server is running at 9096 port.
map[Form:map[client_id:[222222] redirect_uri:[http://localhost:9094/oauth2] response_type:[code] scope:[all] state:[xyz]]]
&{9f570e6b48d181cb6a9b59929b279254 {63636040007 227949400 0x9cec00} map[Form:map[redirect_uri:[http://localhost:9094/oauth2] response_type:[code] scope:[all] state:[xyz] client_id:[222222]]] {{0 0} 0 0 0 0}}
map[LoggedInUserID:000000]
&{176c615e89a3a9756422f5a342a31be2 {63636040021 127186000 0x9cec00} map[LoggedInUserID:000000] {{0 0} 0 0 0 0}}
&{36c38ee3f67630094bce8bf81c6d13a3 {63636040025 769267000 0x9cec00} map[] {{0 0} 0 0 0 0}}
例子中session好像存储不了数据
Hi, I would like the ability to extend and implement custom grant types.
My use case is to implement a 'social' grant type like the following lib:
https://github.com/coderello/laravel-passport-social-grant
I'm currently porting an oauth implementation for an app into golang and need to emulate the above.
I don't think this is possible currently in go-oauth2, would you be open to it?
vendor/gopkg.in/oauth2.v3/generates/access.go:28:65: not enough arguments in call to uuid.Must
have (uuid.UUID)
want (uuid.UUID, error)
vendor/gopkg.in/oauth2.v3/generates/access.go:31:67: not enough arguments in call to uuid.Must
have (uuid.UUID)
want (uuid.UUID, error)
vendor/gopkg.in/oauth2.v3/generates/authorize.go:24:31: not enough arguments in call to uuid.Must
have (uuid.UUID)
want (uuid.UUID, error)
vendor/gopkg.in/oauth2.v3/store/token.go:46:23: not enough arguments in call to uuid.Must
have (uuid.UUID)
want (uuid.UUID, error)
Example from generates/access.go
access = base64.URLEncoding.EncodeToString(uuid.NewV3(uuid.Must(uuid.NewV4()), buf.String()).Bytes())
Fixed code:
access = base64.URLEncoding.EncodeToString(uuid.NewV3(uuid.Must(uuid.NewV4(), err), buf.String()).Bytes())
当用密码验证后返回refresh token1,然后用refresh token1去刷新;然后再用密码验证返回refresh token2,然后发现用refresh token1和refresh token2都可以进行刷新,是否应该生成refresh token2的时候要使refresh toekn1失效才合理?还是说我有什么地方配置错误?
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.