{"id":15355070,"url":"https://github.com/adhocore/goic","last_synced_at":"2025-09-13T19:30:09.683Z","repository":{"id":46273361,"uuid":"415849292","full_name":"adhocore/goic","owner":"adhocore","description":"Golang OpenID Connect Client","archived":false,"fork":false,"pushed_at":"2025-03-21T23:40:42.000Z","size":98,"stargazers_count":31,"open_issues_count":3,"forks_count":3,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-22T00:25:42.665Z","etag":null,"topics":["adhocore","claims","go-oidc","golang","google-authentication","json","jwt","login-with-google","login-with-microsoft","login-with-yahoo","oauth2","oidc","oidc-client","openid","openid-client","openid-connect"],"latest_commit_sha":null,"homepage":"https://github.com/adhocore/goic","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adhocore.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"adhocore","custom":["https://paypal.me/ji10"]}},"created_at":"2021-10-11T08:55:25.000Z","updated_at":"2025-03-21T23:39:45.000Z","dependencies_parsed_at":"2024-04-13T07:37:59.212Z","dependency_job_id":"96c13257-73b6-4bcc-905a-d09f84bc6e4b","html_url":"https://github.com/adhocore/goic","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adhocore%2Fgoic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adhocore%2Fgoic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adhocore%2Fgoic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adhocore%2Fgoic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adhocore","download_url":"https://codeload.github.com/adhocore/goic/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249017927,"owners_count":21199069,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["adhocore","claims","go-oidc","golang","google-authentication","json","jwt","login-with-google","login-with-microsoft","login-with-yahoo","oauth2","oidc","oidc-client","openid","openid-client","openid-connect"],"created_at":"2024-10-01T12:22:23.239Z","updated_at":"2025-04-15T06:24:06.666Z","avatar_url":"https://github.com/adhocore.png","language":"Go","readme":"# adhocore/goic\n\n[![Latest Version](https://img.shields.io/github/release/adhocore/goic.svg?style=flat-square)](https://github.com/adhocore/goic/releases)\n[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)\n[![Go Report](https://goreportcard.com/badge/github.com/adhocore/goic)](https://goreportcard.com/report/github.com/adhocore/goic)\n[![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Simple+Golang+OpenID+Connect+client\u0026url=https://github.com/adhocore/goic\u0026hashtags=go,golang,openid,oauth,openid-connect,connect,oauth2)\n[![Support](https://img.shields.io/static/v1?label=Support\u0026message=%E2%9D%A4\u0026logo=GitHub)](https://github.com/sponsors/adhocore)\n\u003c!-- [![Donate 15](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square\u0026label=donate+15)](https://www.paypal.me/ji10/15usd)\n[![Donate 25](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square\u0026label=donate+25)](https://www.paypal.me/ji10/25usd)\n[![Donate 50](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square\u0026label=donate+50)](https://www.paypal.me/ji10/50usd) --\u003e\n\n\nGOIC, **Go Open ID Connect**, is OpenID connect client library for Golang.\nIt supports the *Authorization Code Flow* of OpenID Connect specification.\n\n# Installation\n\n```sh\ngo get -u github.com/adhocore/goic\n```\n\n# Usage\n\nDecide an endpoint (aka URI) in your server where you would like `goic` to intercept and add OpenID Connect flow.\nLet's say `/auth/o8`. Then the provider name follows it.\nAll the OpenID providers that your server should support will need a unique name and each of the\nproviders get a URI like so `/auth/o8/\u003cname\u003e`. Example:\n\n| Provider | Name | OpenID URI | Revocation | Signout |\n|----------|------|------------|----------|------------|\n| Google | google | `/auth/o8/google` | Yes | No\n| Facebook | facebook | `/auth/o8/facebook` | No | No\n| Microsoft | microsoft | `/auth/o8/microsoft` | No | Yes\n| Yahoo | yahoo | `/auth/o8/yahoo` | Yes | No\n| Paypal | paypal | `/auth/o8/paypal` | No | No\n\n**Important:** All the providers **must** provide .well-known configurations for OpenID auto discovery.\n\nGet ready with OpenID provider credentials (client id and secret).\nFor Google, check [this](https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid).\nTo use the example below you need to export `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` env vars.\n\nYou also need to configure application domain and redirect URI in the Provider console/dashboard.\n(redirect URI is same as OpenID URI in above table).\n\nBelow is an example for authorization code flow but instead of copy/pasting it entirely you can use it for reference.\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\n\t\"github.com/adhocore/goic\"\n)\n\nfunc main() {\n\t// Init GOIC with a root uri and verbose mode (=true)\n\tg := goic.New(\"/auth/o8\", true)\n\n\t// Register Google provider with name google and its auth URI\n\t// It will preemptively load well-known config and jwks keys\n\tp := g.NewProvider(\"google\", \"https://accounts.google.com\")\n\n\t// Configure credentials for Google provider\n\tp.WithCredential(os.Getenv(\"GOOGLE_CLIENT_ID\"), os.Getenv(\"GOOGLE_CLIENT_SECRET\"))\n\n\t// Configure scope\n\tp.WithScope(\"openid email profile\")\n\n\t// Define a callback that will receive token and user info on successful verification\n\tg.UserCallback(func(t *goic.Token, u *goic.User, w http.ResponseWriter, r *http.Request) {\n\t\t// Persist token and user info as you wish! Be sure to check for error in `u.Error` first\n\t\t// Use the available `w` and `r` params to show some nice page with message to your user\n\t\t// OR redirect them to homepage/dashboard etc\n\n\t\t// However, for the example, here I just dump it in backend console\n\t\tlog.Println(\"token: \", t)\n\t\tlog.Println(\"user: \", u)\n\n\t\t// and tell the user it is all good:\n\t\t_, _ = w.Write([]byte(\"All good, check backend console\"))\n\t})\n\n\t// Listen address for server, 443 for https as OpenID connect mandates it!\n\taddr := \"localhost:443\"\n\t// You need to find a way to run your localhost in HTTPS as well.\n\t// You may also alias it something like `goic.lvh.me` (lvh is local virtual host)\n\t// *.lvh.me is automatically mapped to 127.0.0.1 in unix systems.\n\n\t// A catch-all dummy handler\n\thandler := func(w http.ResponseWriter, r *http.Request) {\n\t\t_, _ = w.Write([]byte(r.Method + \" \" + r.URL.Path))\n\t}\n\n\tlog.Println(\"Server running on https://localhost\")\n\tlog.Println(\"            Visit https://localhost/auth/o8/google\")\n\n\t// This is just example (don't copy it)\n\tuseMux := os.Getenv(\"GOIC_HTTP_MUX\") == \"1\"\n\tif useMux {\n\t\tmux := http.NewServeMux()\n\t\t// If you use http mux, wrap your handler with g.MiddlewareHandler\n\t\tmux.Handle(\"/\", g.MiddlewareHandler(http.HandlerFunc(handler)))\n\t\tserver := \u0026http.Server{Addr: addr, Handler: mux}\n\t\tlog.Fatal(server.ListenAndServeTLS(\"server.crt\", \"server.key\"))\n\t} else {\n\t\t// If you just use plain simple handler func,\n\t\t// wrap your handler with g.MiddlewareFunc\n\t\thttp.HandleFunc(\"/\", g.MiddlewareFunc(handler))\n\t\tlog.Fatal(http.ListenAndServeTLS(addr, \"server.crt\", \"server.key\", nil))\n\t}\n}\n```\n\n```go\n// OR, you can use shorthand syntax to register providers:\n\ng := goic.New(\"/auth/o8\", false)\ng.AddProvider(goic.Google.WithCredential(os.Getenv(\"GOOGLE_CLIENT_ID\"), os.Getenv(\"GOOGLE_CLIENT_SECRET\")))\ng.AddProvider(goic.Microsoft.WithCredential(os.Getenv(\"MICROSOFT_CLIENT_ID\"), os.Getenv(\"MICROSOFT_CLIENT_SECRET\")))\ng.AddProvider(goic.Yahoo.WithCredential(os.Getenv(\"YAHOO_CLIENT_ID\"), os.Getenv(\"YAHOO_CLIENT_SECRET\")))\n\n// ...\n```\n\nAfter having code like that, build the binary (`go build`) and run server program (`./\u003cbinary\u003e`).\n\nYou need to point `Sign in with \u003cprovider\u003e`  button to `https://localhost/auth/o8/\u003cprovider\u003e` for your end user.\nFor example:\n```html\n\u003ca href=\"https://localhost/auth/o8/google\"\u003eSign in with Google\u003c/a\u003e\n\u003ca href=\"https://localhost/auth/o8/microsoft\"\u003eSign in with Microsoft\u003c/a\u003e\n\u003ca href=\"https://localhost/auth/o8/yahoo\"\u003eSign in with Yahoo\u003c/a\u003e\n```\n\nThe complete flow is managed and handled by GOIC for you and on successful verification,\nYou will be able to receive user and token info in your callback via `g.UserCallback`!\nThat is where you persist the user data, set some cookie etc.\n\nCheck [examples](./examples) directory later for more, as it will be updated\nwhen GOIC has new features.\n\n\u003e The example and discussion here assume `localhost` domain so adjust that accordingly for your domains.\n\n### Signing out\n\nFor signing out you need to manually invoke `g.SignOut()` from within http context. See the [API](#signout) below.\nThere is also a working [example](./examples/all.go). Note that not all Providers support signing out.\n\n### Revocation\n\nTo revoke a token manually, invoke `g.RevokeToken()` from any context. See the [API](#revoketoken) below.\nThere is also a working [example](./examples/all.go). Note that not all Providers support revocation.\n\n---\n## GOIC API\n\nGOIC supports full end-to-end for Authorization Code Flow, however if you want to manually interact, here's summary of API:\n\n#### Supports\n\nUse it to check if a provider is supported.\n\n```go\ng := goic.New(\"/auth/o8\", false)\ng.NewProvider(\"abc\", \"...\").WithCredential(\"...\", \"...\")\n\ng.Supports(\"abc\") // true\ng.Supports(\"xyz\") // false\n```\n\n#### RequestAuth\n\nManually request authentication from OpenID Provider. Must be called from within http context.\n\n```go\ng := goic.New(\"/auth/o8\", false)\np := g.NewProvider(\"abc\", \"...\").WithCredential(\"...\", \"...\")\n\n// Generate random unique state and nonce\nstate, nonce := goic.RandomString(24), goic.RandomString(24)\n// You must save them to cookie/session, so it can be retrieved later for crosscheck\n\n// redir is the redirect url in your host for provider of interest\nredir := \"https://localhost/auth/o8/\" + p.Name\n\n// Redirects to provider first and then back to above redir url\n// res = http.ResponseWriter, req = *http.Request\nerr := g.RequestAuth(p, state, nonce, redir, res, req)\n```\n\n#### Authenticate\n\nManually attempt to authenticate after the request comes back from OpenID Provider.\n\n```go\ng := goic.New(\"/auth/o8\", false)\np := g.NewProvider(\"abc\", \"...\").WithCredential(\"...\", \"...\")\n\n// Read openid provider code from query param, and nonce from cookie/session etc\n// PS: Validate that the nonce is relevant to the state sent by openid provider\ncode, nonce := \"\", \"\"\n\n// redir is the redirect url in your host for provider of interest\nredir := \"https://localhost/auth/o8/\" + p.Name\n\ntok, err := g.Authenticate(p, code, nonce, redir)\n```\n\n#### RefreshToken\n\nUse it to request Access token by using refresh token.\n\n```go\ng := goic.New(\"/auth/o8\", false)\n// ... add providers\nold := \u0026goic.Token{RefreshToken: \"your refresh token\", Provider: goic.Microsoft.Name}\ntok, err := g.RefreshToken(old)\n// Do something with new tok.AccessToken\n```\n\n#### Userinfo\n\nManually request Userinfo by using the token returned by Authentication above.\n\n```go\ng := goic.New(\"/auth/o8\", false)\np := g.NewProvider(\"abc\", \"...\").WithCredential(\"...\", \"...\")\n// ...\ntok, err := g.Authenticate(p, code, nonce, redir)\nuser := g.UserInfo(tok)\nerr := user.Error\n```\n\n#### SignOut\n\nUse it to sign out the user from OpenID Provider. Must be called from within http context.\nIdeally, you would clear the session and logout user from your own system first and then invoke SignOut.\n\n```go\ng := goic.New(\"/auth/o8\", false)\np := g.NewProvider(\"abc\", \"...\").WithCredential(\"...\", \"...\")\n// ...\ntok := \u0026goic.Token{AccessToken: \"current session token\", Provider: p.Name}\nerr := g.SignOut(tok, \"http://some/preconfigured/redir/uri\", res, req)\n// redir uri is optional\nerr := g.SignOut(tok, \"\", res, req)\n```\n\n#### RevokeToken\n\nUse it to revoke the token so that is incapacitated.\n\n```go\ng := goic.New(\"/auth/o8\", false)\np := g.NewProvider(\"abc\", \"...\").WithCredential(\"...\", \"...\")\n// ...\ntok := \u0026goic.Token{AccessToken: \"current session token\", Provider: p.Name}\nerr := g.RevokeToken(tok)\n```\n\n---\n### Demo\n\n`GOIC` has been implemented in opensource project [adhocore/urlsh](https://github.com/adhocore/urlsh):\n\n| Provider | Name | Demo URL |\n|----------|------|------------|\n| Google | google | [urlssh.xyz/auth/o8/google](https://urlssh.xyz/auth/o8/google) |\n| Microsoft | microsoft | [urlssh.xyz/auth/o8/microsoft](https://urlssh.xyz/auth/o8/microsoft) |\n| Yahoo | yahoo | [urlssh.xyz/auth/o8/yahoo](https://urlssh.xyz/auth/o8/yahoo) |\n\nOn successful verification your information is [echoed back](https://github.com/adhocore/urlsh/blob/main/router/router.go#L48-L53) to you as JSON but **not** saved in server (pinky promise).\n\n---\n# TODO\n\n- [X] ~~Support refresh token grant_type~~ [Check #2](https://github.com/adhocore/goic/issues/2)\n- [ ] Tests and more tests\n- [ ] Release stable version\n- [x] ~~Support OpenID `Implicit Flow`~~ [Check #3](https://github.com/adhocore/goic/issues/3)\n\n## License\n\n\u003e \u0026copy; [MIT](./LICENSE) | 2021-2099, Jitendra Adhikari\n\n## Credits\n\nRelease managed by [please](https://github.com/adhocore/please).\n\n---\n### Other projects\nMy other golang projects you might find interesting and useful:\n\n- [**gronx**](https://github.com/adhocore/gronx) - Lightweight, fast and dependency-free Cron expression parser (due checker), task scheduler and/or daemon for Golang (tested on v1.13 and above) and standalone usage.\n- [**urlsh**](https://github.com/adhocore/urlsh) - URL shortener and bookmarker service with UI, API, Cache, Hits Counter and forwarder using postgres and redis in backend, bulma in frontend; has [web](https://urlssh.xyz) and cli client\n- [**fast**](https://github.com/adhocore/fast) - Check your internet speed with ease and comfort right from the terminal\n- [**chin**](https://github.com/adhocore/chin) - A GO lang command line tool to show a spinner as user waits for some long running jobs to finish.\n","funding_links":["https://github.com/sponsors/adhocore","https://paypal.me/ji10","https://www.paypal.me/ji10/15usd","https://www.paypal.me/ji10/25usd","https://www.paypal.me/ji10/50usd"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadhocore%2Fgoic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadhocore%2Fgoic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadhocore%2Fgoic/lists"}