├── .gitignore ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── Feature Request.yml │ └── Bug Report.yml ├── dependabot.yml ├── workflows │ └── semgrep.yml └── stale.yml ├── 01-Login ├── .gitignore ├── .dockerignore ├── exec.ps1 ├── exec.sh ├── .env.example ├── web │ ├── static │ │ ├── js │ │ │ ├── user.js │ │ │ └── js.cookie.js │ │ ├── image │ │ │ └── auth0_logo_final_blue_RGB.png │ │ └── css │ │ │ └── app.css │ ├── app │ │ ├── home │ │ │ └── home.go │ │ ├── user │ │ │ └── user.go │ │ ├── logout │ │ │ └── logout.go │ │ ├── login │ │ │ └── login.go │ │ └── callback │ │ │ └── callback.go │ └── template │ │ ├── home.html │ │ └── user.html ├── platform │ ├── middleware │ │ └── isAuthenticated.go │ ├── router │ │ └── router.go │ └── authenticator │ │ └── auth.go ├── Dockerfile ├── main.go ├── go.mod ├── README.md └── go.sum ├── LICENSE ├── README.md └── .circleci └── config.yml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @auth0-samples/dx-sdks-engineer 2 | -------------------------------------------------------------------------------- /01-Login/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .DS_Store 3 | src/ 4 | pkg/ 5 | -------------------------------------------------------------------------------- /01-Login/.dockerignore: -------------------------------------------------------------------------------- 1 | .env.example 2 | .gitignore 3 | README.md 4 | exec.sh 5 | exec.ps1 6 | src/ 7 | pkg/ 8 | -------------------------------------------------------------------------------- /01-Login/exec.ps1: -------------------------------------------------------------------------------- 1 | docker build -t auth0-golang-web-app . 2 | docker run --env-file .env -p 3000:3000 -it auth0-golang-web-app 3 | -------------------------------------------------------------------------------- /01-Login/exec.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | docker build -t auth0-golang-web-app . 3 | docker run --env-file .env -p 3000:3000 -it auth0-golang-web-app 4 | -------------------------------------------------------------------------------- /01-Login/.env.example: -------------------------------------------------------------------------------- 1 | AUTH0_CLIENT_ID={CLIENT_ID} 2 | AUTH0_DOMAIN={DOMAIN} 3 | AUTH0_CLIENT_SECRET={CLIENT_SECRET} 4 | AUTH0_CALLBACK_URL=http://localhost:3000/callback 5 | -------------------------------------------------------------------------------- /01-Login/web/static/js/user.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('.btn-logout').click(function(e) { 3 | Cookies.remove('auth-session'); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /01-Login/web/static/image/auth0_logo_final_blue_RGB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0-samples/auth0-golang-web-app/HEAD/01-Login/web/static/image/auth0_logo_final_blue_RGB.png -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 🤔 Help & Questions 4 | url: https://community.auth0.com 5 | about: Ask general support or usage questions in the Auth0 Community forums. 6 | -------------------------------------------------------------------------------- /01-Login/web/app/home/home.go: -------------------------------------------------------------------------------- 1 | package home 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | // Handler for our home page. 10 | func Handler(ctx *gin.Context) { 11 | ctx.HTML(http.StatusOK, "home.html", nil) 12 | } 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/01-Login" 5 | schedule: 6 | interval: "daily" 7 | ignore: 8 | - dependency-name: "*" 9 | update-types: ["version-update:semver-major", "version-update:semver-patch"] 10 | -------------------------------------------------------------------------------- /01-Login/web/app/user/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-contrib/sessions" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | // Handler for our logged-in user page. 11 | func Handler(ctx *gin.Context) { 12 | session := sessions.Default(ctx) 13 | profile := session.Get("profile") 14 | 15 | ctx.HTML(http.StatusOK, "user.html", profile) 16 | } 17 | -------------------------------------------------------------------------------- /01-Login/platform/middleware/isAuthenticated.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-contrib/sessions" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | // IsAuthenticated is a middleware that checks if 11 | // the user has already been authenticated previously. 12 | func IsAuthenticated(ctx *gin.Context) { 13 | if sessions.Default(ctx).Get("profile") == nil { 14 | ctx.Redirect(http.StatusSeeOther, "/") 15 | } else { 16 | ctx.Next() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /01-Login/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.19.3-alpine3.17 2 | 3 | # Define current working directory 4 | WORKDIR /01-Login 5 | 6 | # Download modules to local cache so we can skip re- 7 | # downloading on consecutive docker build commands 8 | COPY go.mod . 9 | COPY go.sum . 10 | RUN go mod download 11 | 12 | # Add sources 13 | COPY . . 14 | 15 | RUN go build -o out/auth0-go-web-app . 16 | 17 | # Expose port 3000 for our web app binary 18 | EXPOSE 3000 19 | 20 | CMD ["/01-Login/out/auth0-go-web-app"] 21 | -------------------------------------------------------------------------------- /.github/workflows/semgrep.yml: -------------------------------------------------------------------------------- 1 | name: Semgrep 2 | 3 | on: 4 | pull_request: {} 5 | 6 | push: 7 | branches: ["master", "main"] 8 | 9 | schedule: 10 | - cron: '30 0 1,15 * *' 11 | 12 | jobs: 13 | semgrep: 14 | name: Scan 15 | runs-on: ubuntu-latest 16 | container: 17 | image: returntocorp/semgrep 18 | # Skip any PR created by dependabot to avoid permission issues 19 | if: (github.actor != 'dependabot[bot]') 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - run: semgrep ci 24 | env: 25 | SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} 26 | -------------------------------------------------------------------------------- /01-Login/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | 7 | "github.com/joho/godotenv" 8 | 9 | "01-Login/platform/authenticator" 10 | "01-Login/platform/router" 11 | ) 12 | 13 | func main() { 14 | if err := godotenv.Load(); err != nil { 15 | log.Fatalf("Failed to load the env vars: %v", err) 16 | } 17 | 18 | auth, err := authenticator.New() 19 | if err != nil { 20 | log.Fatalf("Failed to initialize the authenticator: %v", err) 21 | } 22 | 23 | rtr := router.New(auth) 24 | 25 | log.Print("Server listening on http://localhost:3000/") 26 | if err := http.ListenAndServe("0.0.0.0:3000", rtr); err != nil { 27 | log.Fatalf("There was an error with the http server: %v", err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /01-Login/web/app/logout/logout.go: -------------------------------------------------------------------------------- 1 | package logout 2 | 3 | import ( 4 | "net/http" 5 | "net/url" 6 | "os" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | // Handler for our logout. 12 | func Handler(ctx *gin.Context) { 13 | logoutUrl, err := url.Parse("https://" + os.Getenv("AUTH0_DOMAIN") + "/v2/logout") 14 | if err != nil { 15 | ctx.String(http.StatusInternalServerError, err.Error()) 16 | return 17 | } 18 | 19 | scheme := "http" 20 | if ctx.Request.TLS != nil { 21 | scheme = "https" 22 | } 23 | 24 | returnTo, err := url.Parse(scheme + "://" + ctx.Request.Host) 25 | if err != nil { 26 | ctx.String(http.StatusInternalServerError, err.Error()) 27 | return 28 | } 29 | 30 | parameters := url.Values{} 31 | parameters.Add("returnTo", returnTo.String()) 32 | parameters.Add("client_id", os.Getenv("AUTH0_CLIENT_ID")) 33 | logoutUrl.RawQuery = parameters.Encode() 34 | 35 | ctx.Redirect(http.StatusTemporaryRedirect, logoutUrl.String()) 36 | } 37 | -------------------------------------------------------------------------------- /01-Login/web/template/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 22 |
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 90 5 | 6 | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. 7 | daysUntilClose: 7 8 | 9 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 10 | exemptLabels: [] 11 | 12 | # Set to true to ignore issues with an assignee (defaults to false) 13 | exemptAssignees: true 14 | 15 | # Label to use when marking as stale 16 | staleLabel: closed:stale 17 | 18 | # Comment to post when marking as stale. Set to `false` to disable 19 | markComment: > 20 | This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you have not received a response for our team (apologies for the delay) and this is still a blocker, please reply with additional information or just a ping. Thank you for your contribution! 🙇‍♂️ -------------------------------------------------------------------------------- /01-Login/web/template/user.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |

19 | 20 |

Welcome {{ .nickname }}

21 | Logout 22 |
23 |
24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /01-Login/web/app/login/login.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/base64" 6 | "net/http" 7 | 8 | "github.com/gin-contrib/sessions" 9 | "github.com/gin-gonic/gin" 10 | 11 | "01-Login/platform/authenticator" 12 | ) 13 | 14 | // Handler for our login. 15 | func Handler(auth *authenticator.Authenticator) gin.HandlerFunc { 16 | return func(ctx *gin.Context) { 17 | state, err := generateRandomState() 18 | if err != nil { 19 | ctx.String(http.StatusInternalServerError, err.Error()) 20 | return 21 | } 22 | 23 | // Save the state inside the session. 24 | session := sessions.Default(ctx) 25 | session.Set("state", state) 26 | if err := session.Save(); err != nil { 27 | ctx.String(http.StatusInternalServerError, err.Error()) 28 | return 29 | } 30 | 31 | ctx.Redirect(http.StatusTemporaryRedirect, auth.AuthCodeURL(state)) 32 | } 33 | } 34 | 35 | func generateRandomState() (string, error) { 36 | b := make([]byte, 32) 37 | _, err := rand.Read(b) 38 | if err != nil { 39 | return "", err 40 | } 41 | 42 | state := base64.StdEncoding.EncodeToString(b) 43 | 44 | return state, nil 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Auth0, Inc. (http://auth0.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /01-Login/platform/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "encoding/gob" 5 | 6 | "github.com/gin-contrib/sessions" 7 | "github.com/gin-contrib/sessions/cookie" 8 | "github.com/gin-gonic/gin" 9 | 10 | "01-Login/platform/authenticator" 11 | "01-Login/platform/middleware" 12 | "01-Login/web/app/callback" 13 | "01-Login/web/app/home" 14 | "01-Login/web/app/login" 15 | "01-Login/web/app/logout" 16 | "01-Login/web/app/user" 17 | ) 18 | 19 | // New registers the routes and returns the router. 20 | func New(auth *authenticator.Authenticator) *gin.Engine { 21 | router := gin.Default() 22 | 23 | // To store custom types in our cookies, 24 | // we must first register them using gob.Register 25 | gob.Register(map[string]interface{}{}) 26 | 27 | store := cookie.NewStore([]byte("secret")) 28 | router.Use(sessions.Sessions("auth-session", store)) 29 | 30 | router.Static("/public", "web/static") 31 | router.LoadHTMLGlob("web/template/*") 32 | 33 | router.GET("/", home.Handler) 34 | router.GET("/login", login.Handler(auth)) 35 | router.GET("/callback", callback.Handler(auth)) 36 | router.GET("/user", middleware.IsAuthenticated, user.Handler) 37 | router.GET("/logout", logout.Handler) 38 | 39 | return router 40 | } 41 | -------------------------------------------------------------------------------- /01-Login/platform/authenticator/auth.go: -------------------------------------------------------------------------------- 1 | package authenticator 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "os" 7 | 8 | "github.com/coreos/go-oidc/v3/oidc" 9 | "golang.org/x/oauth2" 10 | ) 11 | 12 | // Authenticator is used to authenticate our users. 13 | type Authenticator struct { 14 | *oidc.Provider 15 | oauth2.Config 16 | } 17 | 18 | // New instantiates the *Authenticator. 19 | func New() (*Authenticator, error) { 20 | provider, err := oidc.NewProvider( 21 | context.Background(), 22 | "https://"+os.Getenv("AUTH0_DOMAIN")+"/", 23 | ) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | conf := oauth2.Config{ 29 | ClientID: os.Getenv("AUTH0_CLIENT_ID"), 30 | ClientSecret: os.Getenv("AUTH0_CLIENT_SECRET"), 31 | RedirectURL: os.Getenv("AUTH0_CALLBACK_URL"), 32 | Endpoint: provider.Endpoint(), 33 | Scopes: []string{oidc.ScopeOpenID, "profile"}, 34 | } 35 | 36 | return &Authenticator{ 37 | Provider: provider, 38 | Config: conf, 39 | }, nil 40 | } 41 | 42 | // VerifyIDToken verifies that an *oauth2.Token is a valid *oidc.IDToken. 43 | func (a *Authenticator) VerifyIDToken(ctx context.Context, token *oauth2.Token) (*oidc.IDToken, error) { 44 | rawIDToken, ok := token.Extra("id_token").(string) 45 | if !ok { 46 | return nil, errors.New("no id_token field in oauth2 token") 47 | } 48 | 49 | oidcConfig := &oidc.Config{ 50 | ClientID: a.ClientID, 51 | } 52 | 53 | return a.Verifier(oidcConfig).Verify(ctx, rawIDToken) 54 | } 55 | -------------------------------------------------------------------------------- /01-Login/web/app/callback/callback.go: -------------------------------------------------------------------------------- 1 | package callback 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-contrib/sessions" 7 | "github.com/gin-gonic/gin" 8 | 9 | "01-Login/platform/authenticator" 10 | ) 11 | 12 | // Handler for our callback. 13 | func Handler(auth *authenticator.Authenticator) gin.HandlerFunc { 14 | return func(ctx *gin.Context) { 15 | session := sessions.Default(ctx) 16 | if ctx.Query("state") != session.Get("state") { 17 | ctx.String(http.StatusBadRequest, "Invalid state parameter.") 18 | return 19 | } 20 | 21 | // Exchange an authorization code for a token. 22 | token, err := auth.Exchange(ctx.Request.Context(), ctx.Query("code")) 23 | if err != nil { 24 | ctx.String(http.StatusUnauthorized, "Failed to convert an authorization code into a token.") 25 | return 26 | } 27 | 28 | idToken, err := auth.VerifyIDToken(ctx.Request.Context(), token) 29 | if err != nil { 30 | ctx.String(http.StatusInternalServerError, "Failed to verify ID Token.") 31 | return 32 | } 33 | 34 | var profile map[string]interface{} 35 | if err := idToken.Claims(&profile); err != nil { 36 | ctx.String(http.StatusInternalServerError, err.Error()) 37 | return 38 | } 39 | 40 | session.Set("access_token", token.AccessToken) 41 | session.Set("profile", profile) 42 | if err := session.Save(); err != nil { 43 | ctx.String(http.StatusInternalServerError, err.Error()) 44 | return 45 | } 46 | 47 | // Redirect to logged in page. 48 | ctx.Redirect(http.StatusTemporaryRedirect, "/user") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /01-Login/go.mod: -------------------------------------------------------------------------------- 1 | module 01-Login 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/coreos/go-oidc/v3 v3.9.0 7 | github.com/gin-contrib/sessions v0.0.5 8 | github.com/gin-gonic/gin v1.9.1 9 | github.com/joho/godotenv v1.5.1 10 | golang.org/x/oauth2 v0.15.0 11 | ) 12 | 13 | require ( 14 | github.com/bytedance/sonic v1.9.1 // indirect 15 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 16 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 17 | github.com/gin-contrib/sse v0.1.0 // indirect 18 | github.com/go-jose/go-jose/v3 v3.0.1 // indirect 19 | github.com/go-playground/locales v0.14.1 // indirect 20 | github.com/go-playground/universal-translator v0.18.1 // indirect 21 | github.com/go-playground/validator/v10 v10.14.0 // indirect 22 | github.com/goccy/go-json v0.10.2 // indirect 23 | github.com/golang/protobuf v1.5.3 // indirect 24 | github.com/gorilla/context v1.1.1 // indirect 25 | github.com/gorilla/securecookie v1.1.1 // indirect 26 | github.com/gorilla/sessions v1.2.1 // indirect 27 | github.com/json-iterator/go v1.1.12 // indirect 28 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 29 | github.com/leodido/go-urn v1.2.4 // indirect 30 | github.com/mattn/go-isatty v0.0.19 // indirect 31 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 32 | github.com/modern-go/reflect2 v1.0.2 // indirect 33 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 34 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 35 | github.com/ugorji/go/codec v1.2.11 // indirect 36 | golang.org/x/arch v0.3.0 // indirect 37 | golang.org/x/crypto v0.17.0 // indirect 38 | golang.org/x/net v0.19.0 // indirect 39 | golang.org/x/sys v0.15.0 // indirect 40 | golang.org/x/text v0.14.0 // indirect 41 | google.golang.org/appengine v1.6.8 // indirect 42 | google.golang.org/protobuf v1.31.0 // indirect 43 | gopkg.in/yaml.v3 v3.0.1 // indirect 44 | ) 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Auth0 - Go Web App 2 | 3 | [![CircleCI](https://circleci.com/gh/auth0-samples/auth0-golang-web-app.svg?style=svg)](https://circleci.com/gh/auth0-samples/auth0-golang-web-app) 4 | 5 | This sample demonstrates how to add authentication to a Go web app using Auth0. 6 | 7 | Check the [Go Quickstart](https://auth0.com/docs/quickstart/webapp/golang) to better understand this sample. 8 | 9 | ## What is Auth0? 10 | 11 | Auth0 helps you to: 12 | 13 | * Add authentication with [multiple authentication sources](https://docs.auth0.com/identityproviders), either social like **Google, Facebook, Microsoft Account, LinkedIn, GitHub, Twitter, Box, Salesforce, among others**, or enterprise identity systems like **Windows Azure AD, Google Apps, Active Directory, ADFS or any SAML Identity Provider**. 14 | * Add authentication through more traditional **[username/password databases](https://docs.auth0.com/mysql-connection-tutorial)**. 15 | * Add support for **[linking different user accounts](https://docs.auth0.com/link-accounts)** with the same user. 16 | * Support for generating signed [JSON Web Tokens](https://docs.auth0.com/jwt) to call your APIs and **flow the user identity** securely. 17 | * Analytics of how, when and where users are logging in. 18 | * Pull data from other sources and add it to the user profile, through [JavaScript rules](https://docs.auth0.com/rules). 19 | 20 | ## Create a free account in Auth0 21 | 22 | 1. Go to [Auth0](https://auth0.com) and click Sign Up. 23 | 2. Use Google, GitHub or Microsoft Account to login. 24 | 25 | ## Issue Reporting 26 | 27 | If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues. 28 | 29 | ## Author 30 | 31 | [Auth0](https://auth0.com) 32 | 33 | ## License 34 | 35 | This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info. 36 | -------------------------------------------------------------------------------- /01-Login/web/static/css/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "proxima-nova", sans-serif; 3 | text-align: center; 4 | font-size: 300%; 5 | font-weight: 100; 6 | } 7 | input[type=checkbox], 8 | input[type=radio] { 9 | position: absolute; 10 | opacity: 0; 11 | } 12 | input[type=checkbox] + label, 13 | input[type=radio] + label { 14 | display: inline-block; 15 | } 16 | input[type=checkbox] + label:before, 17 | input[type=radio] + label:before { 18 | content: ""; 19 | display: inline-block; 20 | vertical-align: -0.2em; 21 | width: 1em; 22 | height: 1em; 23 | border: 0.15em solid #0074d9; 24 | border-radius: 0.2em; 25 | margin-right: 0.3em; 26 | background-color: white; 27 | } 28 | input[type=radio] + label:before { 29 | border-radius: 50%; 30 | } 31 | input[type=radio]:checked + label:before, 32 | input[type=checkbox]:checked + label:before { 33 | background-color: #0074d9; 34 | box-shadow: inset 0 0 0 0.15em white; 35 | } 36 | input[type=radio]:focus + label:before, 37 | input[type=checkbox]:focus + label:before { 38 | outline: 0; 39 | } 40 | .btn { 41 | font-size: 140%; 42 | text-transform: uppercase; 43 | letter-spacing: 1px; 44 | border: 0; 45 | background-color: #16214D; 46 | color: white; 47 | } 48 | .btn:hover { 49 | background-color: #44C7F4; 50 | } 51 | .btn:focus { 52 | outline: none !important; 53 | } 54 | .btn.btn-lg { 55 | padding: 20px 30px; 56 | } 57 | .btn:disabled { 58 | background-color: #333; 59 | color: #666; 60 | } 61 | h1, 62 | h2, 63 | h3 { 64 | font-weight: 100; 65 | } 66 | #logo img { 67 | width: 300px; 68 | margin-bottom: 60px; 69 | } 70 | .home-description { 71 | font-weight: 100; 72 | margin: 100px 0; 73 | } 74 | h2 { 75 | margin-top: 30px; 76 | margin-bottom: 40px; 77 | font-size: 200%; 78 | } 79 | label { 80 | font-size: 100%; 81 | font-weight: 300; 82 | } 83 | .btn-next { 84 | margin-top: 30px; 85 | } 86 | .answer { 87 | width: 70%; 88 | margin: auto; 89 | text-align: left; 90 | padding-left: 10%; 91 | margin-bottom: 20px; 92 | } 93 | .login-page .login-box { 94 | padding: 100px 0; 95 | } 96 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature Request.yml: -------------------------------------------------------------------------------- 1 | name: 🧩 Feature request 2 | description: Suggest an idea or a feature for this sample 3 | labels: ["feature request"] 4 | 5 | body: 6 | - type: checkboxes 7 | id: checklist 8 | attributes: 9 | label: Checklist 10 | options: 11 | - label: I have looked into the [Readme](https://github.com/auth0-samples/auth0-golang-web-app/tree/master/01-Login#readme) and have not found a suitable solution or answer. 12 | required: true 13 | - label: I have searched the [issues](https://github.com/auth0-samples/auth0-golang-web-app/issues) and have not found a suitable solution or answer. 14 | required: true 15 | - label: I have searched the [Auth0 Community](https://community.auth0.com) forums and have not found a suitable solution or answer. 16 | required: true 17 | - label: I agree to the terms within the [Auth0 Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md). 18 | required: true 19 | 20 | - type: textarea 21 | id: description 22 | attributes: 23 | label: Describe the problem you'd like to have solved 24 | description: A clear and concise description of what the problem is. 25 | validations: 26 | required: true 27 | 28 | - type: textarea 29 | id: ideal-solution 30 | attributes: 31 | label: Describe the ideal solution 32 | description: A clear and concise description of what you want to happen. 33 | validations: 34 | required: true 35 | 36 | - type: textarea 37 | id: alternatives-and-workarounds 38 | attributes: 39 | label: Alternatives and current workarounds 40 | description: A clear and concise description of any alternatives you've considered or any workarounds that are currently in place. 41 | validations: 42 | required: false 43 | 44 | - type: textarea 45 | id: additional-context 46 | attributes: 47 | label: Additional context 48 | description: Add any other context or screenshots about the feature request here. 49 | validations: 50 | required: false 51 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug Report.yml: -------------------------------------------------------------------------------- 1 | name: 🐞 Report a bug 2 | description: Have you found a bug or issue? Create a bug report for this sample 3 | 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | **Please do not report security vulnerabilities here**. The [Responsible Disclosure Program](https://auth0.com/responsible-disclosure-policy) details the procedure for disclosing security issues. 9 | 10 | - type: checkboxes 11 | id: checklist 12 | attributes: 13 | label: Checklist 14 | options: 15 | - label: I have looked into the [Readme](https://github.com/auth0-samples/auth0-golang-web-app/tree/master/01-Login#readme) and have not found a suitable solution or answer. 16 | required: true 17 | - label: I have searched the [issues](https://github.com/auth0-samples/auth0-golang-web-app/issues) and have not found a suitable solution or answer. 18 | required: true 19 | - label: I have searched the [Auth0 Community](https://community.auth0.com) forums and have not found a suitable solution or answer. 20 | required: true 21 | - label: I agree to the terms within the [Auth0 Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md). 22 | required: true 23 | 24 | - type: textarea 25 | id: description 26 | attributes: 27 | label: Description 28 | description: Provide a clear and concise description of the issue, including what you expected to happen. 29 | validations: 30 | required: true 31 | 32 | - type: textarea 33 | id: reproduction 34 | attributes: 35 | label: Reproduction 36 | description: Detail the steps taken to reproduce this error, and whether this issue can be reproduced consistently or if it is intermittent. 37 | placeholder: | 38 | 1. Step 1... 39 | 2. Step 2... 40 | 3. ... 41 | validations: 42 | required: true 43 | 44 | - type: textarea 45 | id: additional-context 46 | attributes: 47 | label: Additional context 48 | description: Any other relevant information you think would be useful. 49 | validations: 50 | required: false 51 | -------------------------------------------------------------------------------- /01-Login/README.md: -------------------------------------------------------------------------------- 1 | # Auth0 + Go Web App Sample 2 | 3 | This sample demonstrates how to add authentication to a Go web app using Auth0. 4 | 5 | Check the [Go Quickstart](https://auth0.com/docs/quickstart/webapp/golang) to better understand this sample. 6 | 7 | ## Running the App 8 | 9 | To run the app, make sure you have **go** installed. 10 | 11 | Rename the `.env.example` file to `.env` and provide your Auth0 credentials. 12 | 13 | ```bash 14 | # .env 15 | 16 | AUTH0_CLIENT_ID={CLIENT_ID} 17 | AUTH0_DOMAIN={DOMAIN} 18 | AUTH0_CLIENT_SECRET={CLIENT_SECRET} 19 | AUTH0_CALLBACK_URL=http://localhost:3000/callback 20 | ``` 21 | 22 | Once you've set your Auth0 credentials in the `.env` file, run `go mod vendor` to download the Go dependencies. 23 | 24 | Run `go run main.go` to start the app and navigate to [http://localhost:3000/](http://localhost:3000/). 25 | 26 | ## What is Auth0? 27 | 28 | Auth0 helps you to: 29 | 30 | * Add authentication with [multiple authentication sources](https://auth0.com/docs/authenticate/identity-providers), either social like **Google, Facebook, Microsoft Account, LinkedIn, GitHub, Twitter, Box, Salesforce, amont others**, or enterprise identity systems like **Windows Azure AD, Google Apps, Active Directory, ADFS or any SAML Identity Provider**. 31 | * Add authentication through more traditional **[username/password databases](https://auth0.com/docs/authenticate/database-connections/custom-db/create-db-connection)**. 32 | * Add support for **[linking different user accounts](https://auth0.com/docs/manage-users/user-accounts/user-account-linking/link-user-accounts)** with the same user. 33 | * Support for generating signed [Json Web Tokens](https://auth0.com/docs/secure/tokens/json-web-tokens) to call your APIs and **flow the user identity** securely. 34 | * Analytics of how, when and where users are logging in. 35 | * Pull data from other sources and add it to the user profile, through [JavaScript rules](https://auth0.com/docs/customize/rules). 36 | 37 | ## Create a free Auth0 account 38 | 39 | 1. Go to [Auth0](https://auth0.com/signup) and click Sign Up. 40 | 2. Use Google, GitHub or Microsoft Account to login. 41 | 42 | ## Issue Reporting 43 | 44 | If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues. 45 | 46 | ## Author 47 | 48 | [Auth0](https://auth0.com) 49 | 50 | ## License 51 | 52 | This project is licensed under the MIT license. See the [LICENSE](LICENSE.txt) file for more info. 53 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Common logic 2 | defaults: &defaults 3 | steps: 4 | - attach_workspace: 5 | at: ~/ 6 | - run: 7 | name: Replace Auth0 test credentials 8 | command: | 9 | mv $AUTH0_CFG.example $AUTH0_CFG 10 | sed -i 's/{CLIENT_ID}/'$AUTH0_TEST_CLIENT_ID'/g' $AUTH0_CFG 11 | sed -i 's/{DOMAIN}/'$AUTH0_TEST_DOMAIN'/g' $AUTH0_CFG 12 | sed -i 's/{CLIENT_SECRET}/'$AUTH0_TEST_CLIENT_SECRET'/g' $AUTH0_CFG 13 | - run: 14 | name: Build pull request 15 | command: | 16 | docker build -t $CIRCLE_JOB ./$SAMPLE_PATH 17 | docker run -d -p 3000:3000 --name $CIRCLE_SHA1 --env-file ./$AUTH0_CFG $CIRCLE_JOB 18 | background: true 19 | - run: 20 | name: Wait for app to be available 21 | command: | 22 | sleep 40 23 | docker run --network host --rm appropriate/curl --retry 8 --retry-connrefused -v localhost:3000 24 | - run: 25 | name: Run tests 26 | command: | 27 | docker create --network host --name tester codeceptjs/codeceptjs codeceptjs run-multiple --all --steps 28 | docker cp $(pwd)/lock_login_test.js tester:/tests/lock_login_test.js 29 | docker cp $(pwd)/codecept.conf.js tester:/tests/codecept.conf.js 30 | docker start -i tester 31 | working_directory: scripts 32 | - run: 33 | name: Copy app container logs 34 | command: | 35 | mkdir -p /tmp/out 36 | docker logs $CIRCLE_SHA1 > /tmp/out/app_logs.log 37 | docker cp tester:/tests/out /tmp/ 38 | when: on_fail 39 | - store_artifacts: 40 | path: /tmp/out 41 | 42 | # Jobs and Workflows 43 | version: 2.1 44 | parameters: 45 | machine_image: 46 | type: string 47 | default: ubuntu-2004:202201-02 48 | jobs: 49 | checkout: 50 | machine: 51 | image: << pipeline.parameters.machine_image >> 52 | steps: 53 | - checkout 54 | - run: git clone https://github.com/auth0-samples/spa-quickstarts-tests scripts 55 | - persist_to_workspace: 56 | root: ~/ 57 | paths: 58 | - project 59 | - scripts 60 | login: 61 | machine: 62 | image: << pipeline.parameters.machine_image >> 63 | environment: 64 | - AUTH0_CFG: 01-Login/.env 65 | - SAMPLE_PATH: 01-Login 66 | <<: *defaults 67 | 68 | workflows: 69 | version: 2 70 | quickstarts_login: 71 | jobs: 72 | - checkout: 73 | context: Quickstart Web App Test 74 | - login: 75 | context: Quickstart Web App Test 76 | requires: 77 | - checkout 78 | -------------------------------------------------------------------------------- /01-Login/web/static/js/js.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * JavaScript Cookie v2.1.3 3 | * https://github.com/js-cookie/js-cookie 4 | * 5 | * Copyright 2006, 2015 Klaus Hartl & Fagner Brack 6 | * Released under the MIT license 7 | */ 8 | ;(function (factory) { 9 | var registeredInModuleLoader = false; 10 | if (typeof define === 'function' && define.amd) { 11 | define(factory); 12 | registeredInModuleLoader = true; 13 | } 14 | if (typeof exports === 'object') { 15 | module.exports = factory(); 16 | registeredInModuleLoader = true; 17 | } 18 | if (!registeredInModuleLoader) { 19 | var OldCookies = window.Cookies; 20 | var api = window.Cookies = factory(); 21 | api.noConflict = function () { 22 | window.Cookies = OldCookies; 23 | return api; 24 | }; 25 | } 26 | }(function () { 27 | function extend () { 28 | var i = 0; 29 | var result = {}; 30 | for (; i < arguments.length; i++) { 31 | var attributes = arguments[ i ]; 32 | for (var key in attributes) { 33 | result[key] = attributes[key]; 34 | } 35 | } 36 | return result; 37 | } 38 | 39 | function init (converter) { 40 | function api (key, value, attributes) { 41 | var result; 42 | if (typeof document === 'undefined') { 43 | return; 44 | } 45 | 46 | // Write 47 | 48 | if (arguments.length > 1) { 49 | attributes = extend({ 50 | path: '/' 51 | }, api.defaults, attributes); 52 | 53 | if (typeof attributes.expires === 'number') { 54 | var expires = new Date(); 55 | expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5); 56 | attributes.expires = expires; 57 | } 58 | 59 | try { 60 | result = JSON.stringify(value); 61 | if (/^[\{\[]/.test(result)) { 62 | value = result; 63 | } 64 | } catch (e) {} 65 | 66 | if (!converter.write) { 67 | value = encodeURIComponent(String(value)) 68 | .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent); 69 | } else { 70 | value = converter.write(value, key); 71 | } 72 | 73 | key = encodeURIComponent(String(key)); 74 | key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent); 75 | key = key.replace(/[\(\)]/g, escape); 76 | 77 | return (document.cookie = [ 78 | key, '=', value, 79 | attributes.expires ? '; expires=' + attributes.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE 80 | attributes.path ? '; path=' + attributes.path : '', 81 | attributes.domain ? '; domain=' + attributes.domain : '', 82 | attributes.secure ? '; secure' : '' 83 | ].join('')); 84 | } 85 | 86 | // Read 87 | 88 | if (!key) { 89 | result = {}; 90 | } 91 | 92 | // To prevent the for loop in the first place assign an empty array 93 | // in case there are no cookies at all. Also prevents odd result when 94 | // calling "get()" 95 | var cookies = document.cookie ? document.cookie.split('; ') : []; 96 | var rdecode = /(%[0-9A-Z]{2})+/g; 97 | var i = 0; 98 | 99 | for (; i < cookies.length; i++) { 100 | var parts = cookies[i].split('='); 101 | var cookie = parts.slice(1).join('='); 102 | 103 | if (cookie.charAt(0) === '"') { 104 | cookie = cookie.slice(1, -1); 105 | } 106 | 107 | try { 108 | var name = parts[0].replace(rdecode, decodeURIComponent); 109 | cookie = converter.read ? 110 | converter.read(cookie, name) : converter(cookie, name) || 111 | cookie.replace(rdecode, decodeURIComponent); 112 | 113 | if (this.json) { 114 | try { 115 | cookie = JSON.parse(cookie); 116 | } catch (e) {} 117 | } 118 | 119 | if (key === name) { 120 | result = cookie; 121 | break; 122 | } 123 | 124 | if (!key) { 125 | result[name] = cookie; 126 | } 127 | } catch (e) {} 128 | } 129 | 130 | return result; 131 | } 132 | 133 | api.set = api; 134 | api.get = function (key) { 135 | return api.call(api, key); 136 | }; 137 | api.getJSON = function () { 138 | return api.apply({ 139 | json: true 140 | }, [].slice.call(arguments)); 141 | }; 142 | api.defaults = {}; 143 | 144 | api.remove = function (key, attributes) { 145 | api(key, '', extend(attributes, { 146 | expires: -1 147 | })); 148 | }; 149 | 150 | api.withConverter = init; 151 | 152 | return api; 153 | } 154 | 155 | return init(function () {}); 156 | })); -------------------------------------------------------------------------------- /01-Login/go.sum: -------------------------------------------------------------------------------- 1 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 2 | github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= 3 | github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 4 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 5 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= 6 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 7 | github.com/coreos/go-oidc/v3 v3.9.0 h1:0J/ogVOd4y8P0f0xUh8l9t07xRP/d8tccvjHl2dcsSo= 8 | github.com/coreos/go-oidc/v3 v3.9.0/go.mod h1:rTKz2PYwftcrtoCzV5g5kvfJoWcm0Mk8AF8y1iAQro4= 9 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 11 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 13 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 14 | github.com/gin-contrib/sessions v0.0.5 h1:CATtfHmLMQrMNpJRgzjWXD7worTh7g7ritsQfmF+0jE= 15 | github.com/gin-contrib/sessions v0.0.5/go.mod h1:vYAuaUPqie3WUSsft6HUlCjlwwoJQs97miaG2+7neKY= 16 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 17 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 18 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 19 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 20 | github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= 21 | github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= 22 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 23 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 24 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 25 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 26 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 27 | github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= 28 | github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 29 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 30 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 31 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 32 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 33 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 34 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 35 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 36 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 37 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 38 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 39 | github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= 40 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 41 | github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= 42 | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= 43 | github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= 44 | github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= 45 | github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= 46 | github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 47 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 48 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 49 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 50 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= 51 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 52 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 53 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 54 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 55 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 56 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 57 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 58 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 59 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 60 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 61 | github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= 62 | github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 63 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 64 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 65 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 66 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 67 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 68 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 69 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 70 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 71 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 72 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 73 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 74 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 75 | github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= 76 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 77 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 78 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 79 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 80 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 81 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 82 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 83 | golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= 84 | golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 85 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 86 | golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 87 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 88 | golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= 89 | golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= 90 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 91 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 92 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 93 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 94 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 95 | golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= 96 | golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= 97 | golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= 98 | golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= 99 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 100 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 101 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 102 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 103 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 104 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 105 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 106 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 107 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 108 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 109 | golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= 110 | golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 111 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 112 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 113 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 114 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 115 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 116 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 117 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 118 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 119 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 120 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 121 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 122 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 123 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 124 | google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= 125 | google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= 126 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 127 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 128 | google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= 129 | google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 130 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 131 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 132 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 133 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 134 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 135 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 136 | --------------------------------------------------------------------------------