├── .gitignore ├── README.md ├── go-api ├── .env ├── auth │ └── auth.go ├── db │ └── db.go ├── email_template.html ├── go.mod ├── go.sum ├── main.go ├── models │ ├── domains │ │ └── user.go │ └── dtos │ │ └── dtos.go └── services │ ├── auth.go │ ├── email.go │ └── user.go └── next-web ├── .env ├── auth ├── credential.tsx └── google.ts ├── components ├── Layout.tsx ├── LoginForm.tsx └── SignupForm.tsx ├── dtos ├── auth.ts └── user.ts ├── next-env.d.ts ├── next.config.js ├── package.json ├── pages ├── _app.tsx ├── api │ └── auth │ │ └── [...nextauth].tsx ├── auth │ ├── email-verify.tsx │ ├── error.tsx │ ├── login.tsx │ └── signup.tsx ├── index.tsx └── profile.tsx ├── public ├── favicon.ico └── vercel.svg ├── services └── backend.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | #### GO Lang ignores 2 | # Binaries for programs and plugins 3 | *.exe 4 | *.exe~ 5 | *.dll 6 | *.so 7 | *.dylib 8 | 9 | # Test binary, built with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | # Dependency directories (remove the comment below to include it) 16 | # vendor/ 17 | .DS_Store 18 | 19 | #Misc 20 | *.db 21 | 22 | 23 | #### NextJS ignores 24 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 25 | # Logs 26 | logs 27 | *.log 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | lerna-debug.log* 32 | .pnpm-debug.log* 33 | 34 | # Diagnostic reports (https://nodejs.org/api/report.html) 35 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 36 | 37 | # Runtime data 38 | pids 39 | *.pid 40 | *.seed 41 | *.pid.lock 42 | 43 | # Directory for instrumented libs generated by jscoverage/JSCover 44 | lib-cov 45 | 46 | # Coverage directory used by tools like istanbul 47 | coverage 48 | *.lcov 49 | 50 | # nyc test coverage 51 | .nyc_output 52 | 53 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 54 | .grunt 55 | 56 | # Bower dependency directory (https://bower.io/) 57 | bower_components 58 | 59 | # node-waf configuration 60 | .lock-wscript 61 | 62 | # Compiled binary addons (https://nodejs.org/api/addons.html) 63 | build/Release 64 | 65 | # Dependency directories 66 | node_modules/ 67 | jspm_packages/ 68 | 69 | # Snowpack dependency directory (https://snowpack.dev/) 70 | web_modules/ 71 | 72 | # TypeScript cache 73 | *.tsbuildinfo 74 | 75 | # Optional npm cache directory 76 | .npm 77 | 78 | # Optional eslint cache 79 | .eslintcache 80 | 81 | # Microbundle cache 82 | .rpt2_cache/ 83 | .rts2_cache_cjs/ 84 | .rts2_cache_es/ 85 | .rts2_cache_umd/ 86 | 87 | # Optional REPL history 88 | .node_repl_history 89 | 90 | # Output of 'npm pack' 91 | *.tgz 92 | 93 | # Yarn Integrity file 94 | .yarn-integrity 95 | 96 | # dotenv environment variables file 97 | # .env 98 | .env.test 99 | .env.production 100 | .env.local* 101 | 102 | # parcel-bundler cache (https://parceljs.org/) 103 | .cache 104 | .parcel-cache 105 | 106 | # Next.js build output 107 | .next 108 | out 109 | 110 | # Nuxt.js build / generate output 111 | .nuxt 112 | dist 113 | 114 | # Gatsby files 115 | .cache/ 116 | # Comment in the public line in if your project uses Gatsby and not Next.js 117 | # https://nextjs.org/blog/next-9-1#public-directory-support 118 | # public 119 | 120 | # vuepress build output 121 | .vuepress/dist 122 | 123 | # Serverless directories 124 | .serverless/ 125 | 126 | # FuseBox cache 127 | .fusebox/ 128 | 129 | # DynamoDB Local files 130 | .dynamodb/ 131 | 132 | # TernJS port file 133 | .tern-port 134 | 135 | # Stores VSCode versions used for testing VSCode extensions 136 | .vscode-test 137 | 138 | # yarn v2 139 | .yarn/cache 140 | .yarn/unplugged 141 | .yarn/build-state.yml 142 | .yarn/install-state.gz 143 | .pnp.* 144 | 145 | 146 | # misc 147 | .DS_Store 148 | *.pem 149 | postman_collections/ 150 | server/ 151 | .idea/ 152 | uploads -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## NextAuth + Custom Backend Authentication 2 | 3 | This is an experimental project to implement custom backend authentication adapter for NextAuth (NextJS Authentication). The backend is developed by Golang + Go-Chi 4 | 5 | 6 | 7 | > :exclamation:Warning: This is an experimental project, codebase is not structured for production grade system 8 | 9 | 10 | 11 | **Experiment Target** 12 | 13 | Experiment target is using a custom backend service to manage signup and login for social account as well as credential account. 14 | 15 | * Before login a user must signup into that system using social account (google) or credential 16 | * User must have to login with same provider after signup 17 | * If someone create account by using google account, then the user must have to use google account while logging into that system 18 | * If someone create account by using credential, then the user must have to use credential to logging into that account 19 | 20 | 21 | 22 | **Setup** 23 | 24 | * Google login setup 25 | * To enable google login use your own google OAuth credential in `next-web/.env` file 26 | * Set google `redirect_url` for both login and signup callback. This `google-signin` and `google-signup` values should be same as our `GoogleProvider` ids. Follow `next-web/pages/api/auth/[...nextauth].tsx` 27 | * `http://localhost:3000/api/auth/callback/google-signin` 28 | * `http://localhost:3000/api/auth/callback/google-signup` 29 | 30 | * Email verification setup 31 | * To send email verification url to credential based registered user use your own email host configuration in `go-api/.env` file 32 | -------------------------------------------------------------------------------- /go-api/.env: -------------------------------------------------------------------------------- 1 | EMAIL_HOST_ADDRESS="smtp.gmail.com" 2 | EMAIL_HOST_PORT="587" 3 | EMAIL_USER_ID="YOUR_EMAIL" 4 | EMAIL_USER_PASSWORD="YOUR_PASSWORD" -------------------------------------------------------------------------------- /go-api/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "auth-api/models/domains" 5 | "auth-api/models/dtos" 6 | "context" 7 | "encoding/json" 8 | "errors" 9 | "net/http" 10 | "strings" 11 | "time" 12 | 13 | "github.com/golang-jwt/jwt" 14 | ) 15 | 16 | const JWT_SECRET = "abcefghijklmopqrst" 17 | const JWT_TTL_HOUR = 24 18 | 19 | const ( 20 | KeyUser = "user" 21 | KeyUserID = "id" 22 | KeyTokenExpired = "exp" 23 | ) 24 | 25 | type AuthUser struct { 26 | ID int 27 | Role int 28 | } 29 | 30 | func Authenticate(next http.Handler) http.Handler { 31 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 32 | extractToken := func() string { 33 | bearerToken := r.Header.Get("Authorization") 34 | strArr := strings.Split(bearerToken, " ") 35 | if len(strArr) == 2 { 36 | return strArr[1] 37 | } 38 | return "" 39 | } 40 | if r.Header["Authorization"] != nil { 41 | tokenString := extractToken() 42 | token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { 43 | //Make sure that the token method conform to "SigningMethodHMAC" 44 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { 45 | return nil, errors.New("invalid authorization token") 46 | } 47 | return []byte(JWT_SECRET), nil 48 | }) 49 | if err != nil { 50 | w.WriteHeader(http.StatusUnauthorized) 51 | json.NewEncoder(w).Encode(&dtos.ErrorDto{Message: err.Error()}) 52 | return 53 | } 54 | if token.Valid { 55 | claims := token.Claims.(jwt.MapClaims) 56 | user := &AuthUser{ 57 | ID: int(claims[KeyUserID].(float64)), 58 | } 59 | userContext := context.WithValue(r.Context(), KeyUser, user) 60 | next.ServeHTTP(w, r.WithContext(userContext)) 61 | } else { 62 | 63 | w.WriteHeader(http.StatusUnauthorized) 64 | json.NewEncoder(w).Encode(&dtos.ErrorDto{Message: errors.New("invalid authorization token").Error()}) 65 | } 66 | } else { 67 | w.WriteHeader(http.StatusUnauthorized) 68 | json.NewEncoder(w).Encode(&dtos.ErrorDto{Message: errors.New("an authorization header is required").Error()}) 69 | } 70 | }) 71 | } 72 | 73 | func GenerateToken(user *domains.User) (string, error) { 74 | token := jwt.New(jwt.SigningMethodHS256) 75 | claims := token.Claims.(jwt.MapClaims) 76 | claims[KeyUserID] = user.ID 77 | claims[KeyTokenExpired] = time.Now().Add(time.Hour * time.Duration(JWT_TTL_HOUR)).Unix() 78 | signedToken, err := token.SignedString([]byte(JWT_SECRET)) 79 | if err != nil { 80 | return "", err 81 | } 82 | return signedToken, nil 83 | } 84 | 85 | func GetAuthUser(r *http.Request) *AuthUser { 86 | user := r.Context().Value(KeyUser) 87 | if user == nil { 88 | return nil 89 | } 90 | return user.(*AuthUser) 91 | } 92 | 93 | func GetEmailVerifierToken(email string) (string, error) { 94 | token := jwt.New(jwt.SigningMethodHS256) 95 | claims := token.Claims.(jwt.MapClaims) 96 | claims[KeyUserID] = email 97 | claims[KeyTokenExpired] = time.Now().Add(time.Hour * time.Duration(JWT_TTL_HOUR)).Unix() 98 | signedToken, err := token.SignedString([]byte(JWT_SECRET)) 99 | if err != nil { 100 | return "", err 101 | } 102 | return signedToken, nil 103 | } 104 | 105 | func GetEmailFromVerifierToken(emailToken string) (string, error) { 106 | token, err := jwt.Parse(emailToken, func(token *jwt.Token) (interface{}, error) { 107 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { 108 | return nil, errors.New("invalid token") 109 | } 110 | return []byte(JWT_SECRET), nil 111 | }) 112 | if err != nil { 113 | return "", err 114 | } 115 | if token.Valid { 116 | claims := token.Claims.(jwt.MapClaims) 117 | return claims[KeyUserID].(string), nil 118 | } 119 | return "", errors.New("invalid token") 120 | } 121 | -------------------------------------------------------------------------------- /go-api/db/db.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "time" 7 | 8 | "gorm.io/driver/sqlite" 9 | "gorm.io/gorm" 10 | 11 | "auth-api/models/domains" 12 | 13 | gormlog "gorm.io/gorm/logger" 14 | ) 15 | 16 | type DB struct { 17 | *gorm.DB 18 | } 19 | 20 | var dbInstance *DB 21 | 22 | func ConnectDB() error { 23 | gormCfg := &gorm.Config{ 24 | DisableNestedTransaction: true, 25 | SkipDefaultTransaction: true, 26 | Logger: gormlog.New( 27 | log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer 28 | gormlog.Config{ 29 | SlowThreshold: time.Second, // Slow SQL threshold 30 | LogLevel: gormlog.Info, // Log level 31 | IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger 32 | Colorful: true, // Disable color 33 | }), 34 | } 35 | 36 | conn, err := gorm.Open(sqlite.Open("auth.db"), gormCfg) 37 | if err != nil { 38 | return err 39 | } 40 | dbInstance = &DB{conn} 41 | return nil 42 | } 43 | 44 | //Migration : auto migrate data models 45 | func (db *DB) Migration() { 46 | db.AutoMigrate( 47 | &domains.User{}, 48 | ) 49 | } 50 | 51 | func GetInstance() *DB { 52 | return dbInstance 53 | } 54 | -------------------------------------------------------------------------------- /go-api/email_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

6 | Hello {{.Name}} Go Next Auth account has been created. Please go to 7 | this following url to verify your account. Token will be expired in 8 | 24hours. 9 |

10 | {{.URL}} 11 |

Thanks

12 | 13 | 14 | -------------------------------------------------------------------------------- /go-api/go.mod: -------------------------------------------------------------------------------- 1 | module auth-api 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/go-chi/chi/v5 v5.0.7 7 | github.com/golang-jwt/jwt v3.2.2+incompatible 8 | golang.org/x/crypto v0.1.0 9 | gorm.io/driver/sqlite v1.4.3 10 | gorm.io/gorm v1.24.0 11 | ) 12 | 13 | require ( 14 | github.com/fsnotify/fsnotify v1.5.4 // indirect 15 | github.com/hashicorp/hcl v1.0.0 // indirect 16 | github.com/jinzhu/inflection v1.0.0 // indirect 17 | github.com/jinzhu/now v1.1.5 // indirect 18 | github.com/magiconair/properties v1.8.6 // indirect 19 | github.com/mattn/go-sqlite3 v1.14.15 // indirect 20 | github.com/mitchellh/mapstructure v1.5.0 // indirect 21 | github.com/pelletier/go-toml v1.9.5 // indirect 22 | github.com/pelletier/go-toml/v2 v2.0.5 // indirect 23 | github.com/spf13/afero v1.8.2 // indirect 24 | github.com/spf13/cast v1.5.0 // indirect 25 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 26 | github.com/spf13/pflag v1.0.5 // indirect 27 | github.com/spf13/viper v1.13.0 // indirect 28 | github.com/subosito/gotenv v1.4.1 // indirect 29 | golang.org/x/sys v0.1.0 // indirect 30 | golang.org/x/text v0.4.0 // indirect 31 | gopkg.in/ini.v1 v1.67.0 // indirect 32 | gopkg.in/yaml.v2 v2.4.0 // indirect 33 | gopkg.in/yaml.v3 v3.0.1 // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /go-api/go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 10 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 11 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 12 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 13 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 14 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 15 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 16 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 17 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 18 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 19 | cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 20 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 21 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 22 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 23 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 24 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 25 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 26 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 27 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 28 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 29 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 30 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 31 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 32 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 33 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 34 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 35 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 36 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 37 | cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= 38 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 39 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 40 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 41 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 42 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 43 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 44 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 45 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 46 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 47 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 48 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 49 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 50 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 51 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 52 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 53 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 54 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 55 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 56 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 57 | github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= 58 | github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= 59 | github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= 60 | github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= 61 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 62 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 63 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 64 | github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= 65 | github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= 66 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 67 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 68 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 69 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 70 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 71 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 72 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 73 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 74 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 75 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 76 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 77 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 78 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 79 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 80 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 81 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 82 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 83 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 84 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 85 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 86 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 87 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 88 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 89 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 90 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 91 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 92 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 93 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 94 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 95 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 96 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 97 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 98 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 99 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 100 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 101 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 102 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 103 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 104 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 105 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 106 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 107 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 108 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 109 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 110 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 111 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 112 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 113 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 114 | github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 115 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 116 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 117 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 118 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 119 | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 120 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 121 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 122 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 123 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 124 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 125 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 126 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 127 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 128 | github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 129 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= 130 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 131 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 132 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 133 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 134 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 135 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 136 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 137 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 138 | github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= 139 | github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 140 | github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= 141 | github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= 142 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 143 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 144 | github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= 145 | github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 146 | github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= 147 | github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= 148 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 149 | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 150 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 151 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 152 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 153 | github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= 154 | github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= 155 | github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= 156 | github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= 157 | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= 158 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 159 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 160 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 161 | github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= 162 | github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= 163 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 164 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 165 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 166 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 167 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 168 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 169 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 170 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 171 | github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= 172 | github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= 173 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 174 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 175 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 176 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 177 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 178 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 179 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 180 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 181 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 182 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 183 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 184 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 185 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 186 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 187 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 188 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 189 | golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 190 | golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= 191 | golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= 192 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 193 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 194 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 195 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 196 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 197 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 198 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 199 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 200 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 201 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 202 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 203 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 204 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 205 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 206 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 207 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 208 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 209 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 210 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 211 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 212 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 213 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 214 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 215 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 216 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 217 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 218 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 219 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 220 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 221 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 222 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 223 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 224 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 225 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 226 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 227 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 228 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 229 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 230 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 231 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 232 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 233 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 234 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 235 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 236 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 237 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 238 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 239 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 240 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 241 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 242 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 243 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 244 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 245 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 246 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 247 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 248 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 249 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 250 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 251 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 252 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 253 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 254 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 255 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 256 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 257 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 258 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 259 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 260 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 261 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 262 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 263 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 264 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 265 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 266 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 267 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 268 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 269 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 270 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 271 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 272 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 273 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 274 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 275 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 276 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 277 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 278 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 279 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 280 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 281 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 282 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 283 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 284 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 285 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 286 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 287 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 288 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 289 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 290 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 291 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 292 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 293 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 294 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 295 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 296 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 297 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 298 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 299 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 300 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 301 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 302 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 303 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 304 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 305 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 306 | golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 307 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 308 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 309 | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 310 | golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= 311 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 312 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 313 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 314 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 315 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 316 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 317 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 318 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 319 | golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= 320 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 321 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 322 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 323 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 324 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 325 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 326 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 327 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 328 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 329 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 330 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 331 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 332 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 333 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 334 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 335 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 336 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 337 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 338 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 339 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 340 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 341 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 342 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 343 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 344 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 345 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 346 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 347 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 348 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 349 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 350 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 351 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 352 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 353 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 354 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 355 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 356 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 357 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 358 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 359 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 360 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 361 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 362 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 363 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 364 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 365 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 366 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 367 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 368 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 369 | golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 370 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 371 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 372 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 373 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 374 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 375 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 376 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 377 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 378 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 379 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 380 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 381 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 382 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 383 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 384 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 385 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 386 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 387 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 388 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 389 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 390 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 391 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 392 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 393 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 394 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 395 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 396 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 397 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 398 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 399 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 400 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 401 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 402 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 403 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 404 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 405 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 406 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 407 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 408 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 409 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 410 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 411 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 412 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 413 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 414 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 415 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 416 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 417 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 418 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 419 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 420 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 421 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 422 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 423 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 424 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 425 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 426 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 427 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 428 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 429 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 430 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 431 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 432 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 433 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 434 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 435 | google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 436 | google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 437 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 438 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 439 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 440 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 441 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 442 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 443 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 444 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 445 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 446 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 447 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 448 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 449 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 450 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 451 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 452 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 453 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 454 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 455 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 456 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 457 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 458 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 459 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 460 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 461 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 462 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 463 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 464 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 465 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 466 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= 467 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 468 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 469 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 470 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 471 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 472 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 473 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 474 | gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU= 475 | gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= 476 | gorm.io/gorm v1.24.0 h1:j/CoiSm6xpRpmzbFJsQHYj+I8bGYWLXVHeYEyyKlF74= 477 | gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= 478 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 479 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 480 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 481 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 482 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 483 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 484 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 485 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 486 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 487 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 488 | -------------------------------------------------------------------------------- /go-api/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "auth-api/auth" 5 | "auth-api/db" 6 | "auth-api/models/dtos" 7 | "auth-api/services" 8 | "encoding/json" 9 | "net/http" 10 | 11 | "github.com/go-chi/chi/v5" 12 | "github.com/go-chi/chi/v5/middleware" 13 | "github.com/spf13/viper" 14 | ) 15 | 16 | func main() { 17 | viper.SetConfigFile(".env") 18 | viper.ReadInConfig() 19 | 20 | db.ConnectDB() 21 | db.GetInstance().Migration() 22 | 23 | router := chi.NewRouter() 24 | router.Use(middleware.Logger) 25 | router.Get("/", func(w http.ResponseWriter, r *http.Request) { 26 | w.Write([]byte("welcome")) 27 | }) 28 | 29 | router.Post("/signup/credential", func(w http.ResponseWriter, r *http.Request) { 30 | var signupData dtos.CredentialSignupDto 31 | parsingErr := json.NewDecoder(r.Body).Decode(&signupData) 32 | if parsingErr != nil { 33 | w.WriteHeader(http.StatusBadRequest) 34 | json.NewEncoder(w).Encode(&dtos.ErrorDto{Message: parsingErr.Error()}) 35 | return 36 | } 37 | 38 | result, err := services.CredentialSignup(&signupData) 39 | if err != nil { 40 | w.WriteHeader(http.StatusBadRequest) 41 | json.NewEncoder(w).Encode(err) 42 | return 43 | } 44 | w.WriteHeader(http.StatusOK) 45 | json.NewEncoder(w).Encode(result) 46 | }) 47 | 48 | router.Post("/signup/oauth", func(w http.ResponseWriter, r *http.Request) { 49 | var signupData dtos.OAuthDto 50 | parsingErr := json.NewDecoder(r.Body).Decode(&signupData) 51 | if parsingErr != nil { 52 | w.WriteHeader(http.StatusBadRequest) 53 | json.NewEncoder(w).Encode(&dtos.ErrorDto{Message: parsingErr.Error()}) 54 | return 55 | } 56 | 57 | result, err := services.OAuthSignup(&signupData) 58 | if err != nil { 59 | w.WriteHeader(http.StatusBadRequest) 60 | json.NewEncoder(w).Encode(err) 61 | return 62 | } 63 | w.WriteHeader(http.StatusOK) 64 | json.NewEncoder(w).Encode(result) 65 | }) 66 | 67 | router.Post("/login/credential", func(w http.ResponseWriter, r *http.Request) { 68 | var signinData dtos.CredentialSigninDto 69 | parsingErr := json.NewDecoder(r.Body).Decode(&signinData) 70 | if parsingErr != nil { 71 | w.WriteHeader(http.StatusBadRequest) 72 | json.NewEncoder(w).Encode(&dtos.ErrorDto{Message: parsingErr.Error()}) 73 | return 74 | } 75 | 76 | token, err := services.UserCredentialLogin(&signinData) 77 | if err != nil { 78 | w.WriteHeader(http.StatusBadRequest) 79 | json.NewEncoder(w).Encode(err) 80 | return 81 | } 82 | w.WriteHeader(http.StatusOK) 83 | json.NewEncoder(w).Encode(token) 84 | }) 85 | 86 | router.Post("/login/oauth", func(w http.ResponseWriter, r *http.Request) { 87 | var signinData dtos.OAuthDto 88 | parsingErr := json.NewDecoder(r.Body).Decode(&signinData) 89 | if parsingErr != nil { 90 | w.WriteHeader(http.StatusBadRequest) 91 | json.NewEncoder(w).Encode(&dtos.ErrorDto{Message: parsingErr.Error()}) 92 | return 93 | } 94 | 95 | token, err := services.UserOAuthLogin(&signinData) 96 | if err != nil { 97 | w.WriteHeader(http.StatusBadRequest) 98 | json.NewEncoder(w).Encode(err) 99 | return 100 | } 101 | w.WriteHeader(http.StatusOK) 102 | json.NewEncoder(w).Encode(token) 103 | }) 104 | 105 | router.With(auth.Authenticate).Get("/profile", func(w http.ResponseWriter, r *http.Request) { 106 | currentUser := auth.GetAuthUser(r) 107 | profile, errDto := services.GetUserByID(currentUser.ID) 108 | if errDto != nil { 109 | w.WriteHeader(http.StatusOK) 110 | json.NewEncoder(w).Encode(errDto) 111 | return 112 | } 113 | w.WriteHeader(http.StatusOK) 114 | json.NewEncoder(w).Encode(profile) 115 | }) 116 | 117 | router.Post("/email-verify", func(w http.ResponseWriter, r *http.Request) { 118 | var verificationData dtos.EmailVerifyDto 119 | parsingErr := json.NewDecoder(r.Body).Decode(&verificationData) 120 | if parsingErr != nil { 121 | w.WriteHeader(http.StatusBadRequest) 122 | json.NewEncoder(w).Encode(&dtos.ErrorDto{Message: parsingErr.Error()}) 123 | return 124 | } 125 | 126 | user, err := services.VerifyUserEmail(&verificationData) 127 | if err != nil { 128 | w.WriteHeader(http.StatusBadRequest) 129 | json.NewEncoder(w).Encode(err) 130 | return 131 | } 132 | w.WriteHeader(http.StatusOK) 133 | json.NewEncoder(w).Encode(user) 134 | }) 135 | 136 | http.ListenAndServe(":8000", router) 137 | 138 | } 139 | -------------------------------------------------------------------------------- /go-api/models/domains/user.go: -------------------------------------------------------------------------------- 1 | package domains 2 | 3 | import "golang.org/x/crypto/bcrypt" 4 | 5 | type User struct { 6 | ID int `gorm:"primarykey"` 7 | Name string `gorm:"type:varchar(256);not null"` 8 | Email string `gorm:"type:varchar(256);not null;unique"` 9 | Phone string `gorm:"type:varchar(20);not null;unique"` 10 | AuthProvider string `gorm:"type:varchar(20);not null"` 11 | Password string `gorm:"type:varchar(512);not null"` 12 | IsEmailVerified bool `gorm:"type:bool"` 13 | } 14 | 15 | const UserTableName = "Users" 16 | 17 | func (User) TableName() string { 18 | return UserTableName 19 | } 20 | 21 | func (user *User) SetPassword(rawPassword string) error { 22 | hashedPassword, err := bcrypt.GenerateFromPassword([]byte(rawPassword), bcrypt.DefaultCost) 23 | if err != nil { 24 | return err 25 | } 26 | user.Password = string(hashedPassword) 27 | return nil 28 | } 29 | 30 | func (user *User) CheckIfPasswordIsCorrect(rawPassword string) bool { 31 | if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(rawPassword)); err != nil { 32 | return false 33 | } 34 | return true 35 | } 36 | -------------------------------------------------------------------------------- /go-api/models/dtos/dtos.go: -------------------------------------------------------------------------------- 1 | package dtos 2 | 3 | type CredentialSignupDto struct { 4 | Name string `json:"name"` 5 | Email string `json:"email"` 6 | Phone string `json:"phone"` 7 | Password string `json:"password"` 8 | } 9 | 10 | type CredentialSigninDto struct { 11 | Email string `json:"email"` 12 | Password string `json:"password"` 13 | } 14 | 15 | type OAuthDto struct { 16 | Provider string `json:"provider"` 17 | AccessToken string `json:"accessToken"` 18 | } 19 | 20 | type AccessDto struct { 21 | Bearer string `json:"bearer"` 22 | } 23 | 24 | type ErrorDto struct { 25 | Message string `json:"message"` 26 | } 27 | 28 | type UserDto struct { 29 | ID int `json:"id"` 30 | Name string `json:"name"` 31 | Email string `json:"email"` 32 | Phone string `json:"phone"` 33 | IsEmailVerified bool `json:"isEmailVerified"` 34 | } 35 | 36 | type RegistrationDto struct { 37 | *UserDto 38 | AccessToken string `json:"accessToken"` 39 | } 40 | 41 | type EmailVerifyDto struct { 42 | Token string `json:"token"` 43 | } 44 | -------------------------------------------------------------------------------- /go-api/services/auth.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "auth-api/auth" 5 | "auth-api/db" 6 | "auth-api/models/domains" 7 | "auth-api/models/dtos" 8 | "encoding/json" 9 | "fmt" 10 | "net/http" 11 | ) 12 | 13 | const ( 14 | GOOGLE_AUTH_PROVIDER = "google" 15 | CREDENTIAL_AUTH_PROVIDER = "credential" 16 | ) 17 | 18 | func CredentialSignup(data *dtos.CredentialSignupDto) (*dtos.RegistrationDto, *dtos.ErrorDto) { 19 | user := &domains.User{ 20 | Name: data.Name, 21 | Phone: data.Phone, 22 | Email: data.Email, 23 | AuthProvider: CREDENTIAL_AUTH_PROVIDER, 24 | IsEmailVerified: false, 25 | } 26 | user.SetPassword(data.Password) 27 | 28 | if err := db.GetInstance().Create(user).Error; err != nil { 29 | return nil, &dtos.ErrorDto{Message: err.Error()} 30 | } 31 | 32 | // dispatching verification email to that user 33 | go func() { 34 | emailToken, _ := auth.GetEmailVerifierToken(data.Email) 35 | DispatchEmail(&EmailMessage{Subject: "Email Verification", Receiver: data.Email, Data: map[string]string{ 36 | "Name": data.Name, 37 | "URL": fmt.Sprintf("http://localhost:3000/auth/email-verify?token=%s", emailToken), 38 | }}) 39 | }() 40 | 41 | token, err := auth.GenerateToken(user) 42 | if err != nil { 43 | return nil, &dtos.ErrorDto{Message: err.Error()} 44 | } 45 | 46 | userDto := &dtos.UserDto{ 47 | ID: user.ID, 48 | Name: user.Name, 49 | Email: user.Email, 50 | Phone: user.Phone, 51 | IsEmailVerified: user.IsEmailVerified, 52 | } 53 | 54 | response := &dtos.RegistrationDto{} 55 | response.UserDto = userDto 56 | response.AccessToken = token 57 | 58 | return response, nil 59 | } 60 | 61 | func OAuthSignup(data *dtos.OAuthDto) (*dtos.RegistrationDto, *dtos.ErrorDto) { 62 | if data.Provider != GOOGLE_AUTH_PROVIDER { 63 | return nil, &dtos.ErrorDto{Message: "system is not support this auth provider"} 64 | } 65 | googleProfile, err := getGoogleProfile(data.AccessToken) 66 | if err != nil { 67 | return nil, &dtos.ErrorDto{Message: err.Error()} 68 | } 69 | 70 | if !(googleProfile["email_verified"].(bool)) { 71 | // this parameter is required to verify specific orgranization/domain based auth 72 | return nil, &dtos.ErrorDto{Message: "email is not verified for signup"} 73 | } 74 | 75 | user := &domains.User{ 76 | Name: googleProfile["name"].(string), 77 | Phone: "", 78 | Email: googleProfile["email"].(string), 79 | AuthProvider: GOOGLE_AUTH_PROVIDER, 80 | IsEmailVerified: true, 81 | } 82 | 83 | if err := db.GetInstance().Create(user).Error; err != nil { 84 | return nil, &dtos.ErrorDto{Message: err.Error()} 85 | } 86 | token, err := auth.GenerateToken(user) 87 | if err != nil { 88 | return nil, &dtos.ErrorDto{Message: err.Error()} 89 | } 90 | 91 | userDto := &dtos.UserDto{ 92 | ID: user.ID, 93 | Name: user.Name, 94 | Email: user.Email, 95 | Phone: user.Phone, 96 | IsEmailVerified: user.IsEmailVerified, 97 | } 98 | 99 | response := &dtos.RegistrationDto{} 100 | response.UserDto = userDto 101 | response.AccessToken = token 102 | 103 | return response, nil 104 | } 105 | 106 | func UserCredentialLogin(data *dtos.CredentialSigninDto) (*dtos.AccessDto, *dtos.ErrorDto) { 107 | user := &domains.User{} 108 | if err := db.GetInstance().Table(domains.UserTableName). 109 | Where("email=? AND auth_provider=?", data.Email, CREDENTIAL_AUTH_PROVIDER). 110 | First(user).Error; err != nil { 111 | return nil, &dtos.ErrorDto{Message: err.Error()} 112 | } 113 | 114 | if !user.CheckIfPasswordIsCorrect(data.Password) { 115 | return nil, &dtos.ErrorDto{Message: "incorrect password"} 116 | } 117 | token, err := auth.GenerateToken(user) 118 | if err != nil { 119 | return nil, &dtos.ErrorDto{Message: err.Error()} 120 | } 121 | return &dtos.AccessDto{Bearer: token}, nil 122 | 123 | } 124 | 125 | func UserOAuthLogin(data *dtos.OAuthDto) (*dtos.AccessDto, *dtos.ErrorDto) { 126 | if data.Provider != GOOGLE_AUTH_PROVIDER { 127 | return nil, &dtos.ErrorDto{Message: "invalid auth provider"} 128 | } 129 | 130 | googleProfile, err := getGoogleProfile(data.AccessToken) 131 | if err != nil { 132 | return nil, &dtos.ErrorDto{Message: err.Error()} 133 | } 134 | 135 | user := &domains.User{} 136 | if err := db.GetInstance().Table(domains.UserTableName). 137 | Where("email=? AND auth_provider=?", googleProfile["email"], GOOGLE_AUTH_PROVIDER). 138 | First(user).Error; err != nil { 139 | return nil, &dtos.ErrorDto{Message: err.Error()} 140 | } 141 | 142 | token, err := auth.GenerateToken(user) 143 | if err != nil { 144 | return nil, &dtos.ErrorDto{Message: err.Error()} 145 | } 146 | return &dtos.AccessDto{Bearer: token}, nil 147 | } 148 | 149 | func getGoogleProfile(token string) (map[string]interface{}, error) { 150 | url := fmt.Sprintf("https://www.googleapis.com/oauth2/v3/userinfo?access_token=%s", token) 151 | httpReq, err := http.NewRequest("GET", url, nil) 152 | if err != nil { 153 | return nil, err 154 | } 155 | 156 | httpClient := &http.Client{} 157 | httpRes, err := httpClient.Do(httpReq) 158 | 159 | if err != nil { 160 | return nil, err 161 | } 162 | 163 | data := make(map[string]interface{}) 164 | 165 | err = json.NewDecoder(httpRes.Body).Decode(&data) 166 | if err != nil { 167 | return nil, err 168 | } 169 | return data, nil 170 | } 171 | -------------------------------------------------------------------------------- /go-api/services/email.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "auth-api/models/dtos" 5 | "bytes" 6 | "fmt" 7 | "html/template" 8 | "net/smtp" 9 | 10 | "github.com/spf13/viper" 11 | ) 12 | 13 | 14 | 15 | type EmailMessage struct { 16 | Subject string 17 | Receiver string 18 | Data map[string]string 19 | } 20 | 21 | func DispatchEmail(message *EmailMessage) *dtos.ErrorDto { 22 | 23 | host := viper.Get("EMAIL_HOST_ADDRESS").(string) 24 | port := viper.Get("EMAIL_HOST_PORT").(string) 25 | account := viper.Get("EMAIL_USER_ID").(string) 26 | password := viper.Get("EMAIL_USER_PASSWORD").(string) 27 | 28 | 29 | toList := []string{message.Receiver} 30 | 31 | // set relative path for main.go file 32 | emailTemplate, _ := template.ParseFiles("./email_template.html") 33 | templateData := struct { 34 | Name string 35 | URL string 36 | }{Name: message.Data["Name"], 37 | URL: message.Data["URL"]} 38 | 39 | var body bytes.Buffer 40 | 41 | mimeHeaders := "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n" 42 | body.Write([]byte(fmt.Sprintf("Subject: %s \n%s\n\n", message.Subject, mimeHeaders))) 43 | 44 | if err := emailTemplate.Execute(&body, templateData); err != nil { 45 | return &dtos.ErrorDto{Message: err.Error()} 46 | } 47 | 48 | auth := smtp.PlainAuth("", account, password, host) 49 | 50 | err := smtp.SendMail(host+":"+port, auth, account, toList, body.Bytes()) 51 | 52 | if err != nil { 53 | return &dtos.ErrorDto{Message: err.Error()} 54 | } 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /go-api/services/user.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "auth-api/auth" 5 | "auth-api/db" 6 | "auth-api/models/domains" 7 | "auth-api/models/dtos" 8 | ) 9 | 10 | func GetUserByID(id int) (*dtos.UserDto, *dtos.ErrorDto) { 11 | user := &domains.User{} 12 | if err := db.GetInstance().Table(domains.UserTableName).First(user, id).Error; err != nil { 13 | return nil, &dtos.ErrorDto{Message: err.Error()} 14 | } 15 | 16 | userDto := &dtos.UserDto{ 17 | ID: user.ID, 18 | Name: user.Name, 19 | Email: user.Email, 20 | Phone: user.Phone, 21 | IsEmailVerified: user.IsEmailVerified, 22 | } 23 | 24 | return userDto, nil 25 | } 26 | 27 | func VerifyUserEmail(data *dtos.EmailVerifyDto) (*dtos.UserDto, *dtos.ErrorDto) { 28 | email, err := auth.GetEmailFromVerifierToken(data.Token) 29 | if err != nil { 30 | return nil, &dtos.ErrorDto{Message: err.Error()} 31 | } 32 | 33 | user := &domains.User{} 34 | if err := db.GetInstance(). 35 | Table(domains.UserTableName). 36 | Where("email=?", email). 37 | First(user).Error; err != nil { 38 | return nil, &dtos.ErrorDto{Message: err.Error()} 39 | } 40 | 41 | if user.IsEmailVerified { 42 | userDto := &dtos.UserDto{ 43 | ID: user.ID, 44 | Name: user.Name, 45 | Email: user.Email, 46 | Phone: user.Phone, 47 | IsEmailVerified: user.IsEmailVerified, 48 | } 49 | 50 | return userDto, nil 51 | } 52 | 53 | user.IsEmailVerified = true 54 | 55 | if err := db.GetInstance().Table(domains.UserTableName).Save(user).Error; err != nil { 56 | return nil, &dtos.ErrorDto{Message: err.Error()} 57 | } 58 | 59 | userDto := &dtos.UserDto{ 60 | ID: user.ID, 61 | Name: user.Name, 62 | Email: user.Email, 63 | Phone: user.Phone, 64 | IsEmailVerified: user.IsEmailVerified, 65 | } 66 | 67 | return userDto, nil 68 | } 69 | -------------------------------------------------------------------------------- /next-web/.env: -------------------------------------------------------------------------------- 1 | GOOGLE_AUTH_CLIENT_ID=client-id 2 | GOOGLE_AUTH_CLIENT_SECRET=client-secret 3 | NEXTAUTH_SECRET=secret-key 4 | BACKEND_API_HOST=http://localhost:8000 -------------------------------------------------------------------------------- /next-web/auth/credential.tsx: -------------------------------------------------------------------------------- 1 | import BackendService from "services/backend"; 2 | 3 | const credentialSigninConfig = { 4 | id: "credentials-signin", 5 | name: "credentials", 6 | credentials: { 7 | email: { label: "Email", type: "text" }, 8 | password: { label: "Password", type: "password" }, 9 | }, 10 | authorize: async (credentials: any, req: any) => { 11 | const { callbackUrl, redirect, ...userData } = credentials; 12 | try { 13 | const loginData = await BackendService.loginCredential(userData); 14 | const profileData = await BackendService.getProfile(loginData.bearer); 15 | return { 16 | ...profileData, 17 | accessToken: loginData.bearer, 18 | }; 19 | } catch (err: any) { 20 | throw new Error(err.message); 21 | } 22 | }, 23 | }; 24 | 25 | const credentialSignupConfig = { 26 | id: "credentials-signup", 27 | name: "credentials", 28 | credentials: { 29 | name: { label: "Name", type: "text" }, 30 | email: { label: "Email", type: "email" }, 31 | phone: { label: "Phone", type: "text" }, 32 | password: { label: "Password", type: "password" }, 33 | }, 34 | authorize: async (credentials: any, req: any) => { 35 | const { callbackUrl, redirect, ...userData } = credentials; 36 | try { 37 | const registrationData = await BackendService.signupCredential(userData); 38 | return registrationData; 39 | } catch (err: any) { 40 | throw new Error(err.message); 41 | } 42 | }, 43 | }; 44 | 45 | const authHandler = async (ctx: any) => { 46 | return ctx.user; 47 | }; 48 | 49 | const getUserProfile = async (ctx: any) => { 50 | const profile = await BackendService.getProfile(ctx.user.accessToken); 51 | return { 52 | ...profile, 53 | accessToken: ctx.user.accessToken, 54 | }; 55 | }; 56 | 57 | const isCredentialProvider = (provider: string) => { 58 | return ( 59 | provider === credentialSigninConfig.id || 60 | provider === credentialSignupConfig.id 61 | ); 62 | }; 63 | 64 | const CredentialAuth = { 65 | signinConfig: credentialSigninConfig, 66 | signupConfig: credentialSignupConfig, 67 | handle: authHandler, 68 | getProfile: getUserProfile, 69 | canProvide: isCredentialProvider, 70 | }; 71 | 72 | export default CredentialAuth; 73 | -------------------------------------------------------------------------------- /next-web/auth/google.ts: -------------------------------------------------------------------------------- 1 | import BackendService from "services/backend"; 2 | 3 | const googleSignupConfig = { 4 | id: "google-signup", 5 | name: "google", 6 | clientId: process.env.GOOGLE_AUTH_CLIENT_ID!, 7 | clientSecret: process.env.GOOGLE_AUTH_CLIENT_SECRET!, 8 | }; 9 | 10 | const googleSigninConfig = { 11 | id: "google-signin", 12 | name: "google", 13 | clientId: process.env.GOOGLE_AUTH_CLIENT_ID!, 14 | clientSecret: process.env.GOOGLE_AUTH_CLIENT_SECRET!, 15 | }; 16 | 17 | const authHandler = async (ctx: any) => { 18 | const authType = ctx.account.provider; 19 | if (authType === googleSigninConfig.id) { 20 | try { 21 | const data = { 22 | provider: "google", 23 | accessToken: ctx.account.access_token, 24 | }; 25 | await BackendService.loginOAuth(data); 26 | return true; 27 | } catch (err: any) { 28 | return `/auth/login?error=${err.message}`; 29 | } 30 | } 31 | 32 | if (authType === googleSignupConfig.id) { 33 | try { 34 | const data = { 35 | provider: "google", 36 | accessToken: ctx.account.access_token, 37 | }; 38 | await BackendService.signupOAuth(data); 39 | return true; 40 | } catch (err: any) { 41 | return `/auth/signup?error=${err.message}`; 42 | } 43 | } 44 | 45 | throw new Error("provider not found"); 46 | }; 47 | 48 | const getUserProfile = async (ctx: any) => { 49 | const data = { 50 | provider: "google", 51 | accessToken: ctx.account.access_token, 52 | }; 53 | 54 | const access = await BackendService.loginOAuth(data); 55 | const profile = await BackendService.getProfile(access.bearer); 56 | 57 | return { 58 | ...profile, 59 | accessToken: access.bearer, 60 | }; 61 | }; 62 | 63 | const isGoogleProvider = (provider: string) => { 64 | return ( 65 | provider === googleSignupConfig.id || provider === googleSigninConfig.id 66 | ); 67 | }; 68 | 69 | const GoogleAuth = { 70 | signinConfig: googleSigninConfig, 71 | signupConfig: googleSignupConfig, 72 | handle: authHandler, 73 | getProfile: getUserProfile, 74 | canProvide: isGoogleProvider, 75 | }; 76 | 77 | export default GoogleAuth; 78 | -------------------------------------------------------------------------------- /next-web/components/Layout.tsx: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | import { signOut, useSession } from "next-auth/react"; 3 | 4 | interface Props { 5 | title?: string; 6 | children?: React.ReactNode; 7 | } 8 | 9 | export const Layout = (props: Props) => { 10 | const { status } = useSession(); 11 | 12 | return ( 13 | <> 14 | 15 | {props.title} 16 | 17 | 28 | {props.children} 29 | 30 | ); 31 | }; 32 | 33 | const AuthNav = () => { 34 | const { data } = useSession(); 35 | return ( 36 |
37 | 38 | {data?.user?.name} 39 | 40 | { 45 | signOut(); 46 | }} 47 | > 48 | Logout 49 | 50 |
51 | ); 52 | }; 53 | 54 | const PublicNav = () => { 55 | return ( 56 |
57 | 58 | Login 59 | 60 | 61 | Signup 62 | 63 |
64 | ); 65 | }; 66 | -------------------------------------------------------------------------------- /next-web/components/LoginForm.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | interface Props { 4 | onSubmit: (data: any) => void; 5 | } 6 | 7 | export const LoginForm = (props: Props) => { 8 | const [email, setEmail] = useState(""); 9 | const [password, setPassword] = useState(""); 10 | 11 | const handleSubmit = () => { 12 | const data = { 13 | email, 14 | password, 15 | }; 16 | 17 | props.onSubmit(data); 18 | }; 19 | 20 | return ( 21 |
{ 23 | e.preventDefault(); 24 | handleSubmit(); 25 | }} 26 | > 27 |
28 | 29 | setEmail(e.target.value)} 36 | /> 37 |
38 | 39 |
40 | 41 | setPassword(e.target.value)} 48 | /> 49 |
50 | 51 |
52 | 59 |
60 |
61 | ); 62 | }; 63 | -------------------------------------------------------------------------------- /next-web/components/SignupForm.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | interface Props { 4 | onSubmit: (data: any) => void; 5 | } 6 | 7 | export const SignupForm = (props: Props) => { 8 | const [name, setName] = useState(""); 9 | const [email, setEmail] = useState(""); 10 | const [phone, setPhone] = useState(""); 11 | const [password, setPassword] = useState(""); 12 | 13 | const handleSubmit = () => { 14 | const data = { 15 | name, 16 | email, 17 | phone, 18 | password, 19 | }; 20 | 21 | props.onSubmit(data); 22 | }; 23 | 24 | return ( 25 |
{ 27 | e.preventDefault(); 28 | handleSubmit(); 29 | }} 30 | > 31 |
32 | 33 | setName(e.target.value)} 40 | /> 41 |
42 | 43 |
44 | 45 | setEmail(e.target.value)} 52 | /> 53 |
54 | 55 |
56 | 57 | setPhone(e.target.value)} 64 | /> 65 |
66 |
67 | 68 | setPassword(e.target.value)} 75 | /> 76 |
77 | 78 |
79 | 86 |
87 |
88 | ); 89 | }; 90 | -------------------------------------------------------------------------------- /next-web/dtos/auth.ts: -------------------------------------------------------------------------------- 1 | export interface OAuthDto { 2 | provider: string; 3 | accessToken: string; 4 | } 5 | 6 | export interface CredentialLoginDto { 7 | email: string; 8 | password: string; 9 | } 10 | 11 | export interface CredentialSignupDto { 12 | name: string; 13 | email: string; 14 | phone: string; 15 | password: string; 16 | } 17 | 18 | export interface AccessDto { 19 | bearer: string; 20 | } 21 | -------------------------------------------------------------------------------- /next-web/dtos/user.ts: -------------------------------------------------------------------------------- 1 | export interface UserDto { 2 | id: number; 3 | name: string; 4 | phone: string; 5 | email: string; 6 | isEmailVerified: boolean; 7 | } 8 | -------------------------------------------------------------------------------- /next-web/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /next-web/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | } 6 | 7 | module.exports = nextConfig 8 | -------------------------------------------------------------------------------- /next-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auth-web", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@types/node": "18.11.8", 13 | "@types/react": "18.0.24", 14 | "@types/react-dom": "18.0.8", 15 | "axios": "^1.1.3", 16 | "bootstrap": "5.2.2", 17 | "next": "13.0.1", 18 | "next-auth": "^4.15.0", 19 | "react": "18.2.0", 20 | "react-dom": "18.2.0", 21 | "react-google-login": "^5.2.2", 22 | "typescript": "4.8.4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /next-web/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import { SessionProvider, useSession } from "next-auth/react"; 2 | 3 | import "bootstrap/dist/css/bootstrap.css"; 4 | 5 | export default function App({ 6 | Component, 7 | pageProps: { session, ...pageProps }, 8 | }: any) { 9 | return ( 10 | 11 | {Component.auth ? ( 12 | 13 | 14 | 15 | ) : ( 16 | 17 | )} 18 | 19 | ); 20 | } 21 | 22 | function Auth(props: any) { 23 | const { status } = useSession({ required: true }); 24 | 25 | if (status === "loading") { 26 | return ( 27 |
28 |
29 |
30 | Loading... 31 |
32 |
33 |
34 | ); 35 | } 36 | 37 | return props.children; 38 | } 39 | -------------------------------------------------------------------------------- /next-web/pages/api/auth/[...nextauth].tsx: -------------------------------------------------------------------------------- 1 | import NextAuth from "next-auth"; 2 | import GoogleProvider from "next-auth/providers/google"; 3 | import CredentialsProvider from "next-auth/providers/credentials"; 4 | import GoogleAuth from "auth/google"; 5 | import CredentialAuth from "auth/credential"; 6 | import BackendService from "services/backend"; 7 | 8 | export const authOption = { 9 | providers: [ 10 | GoogleProvider(GoogleAuth.signinConfig), 11 | GoogleProvider(GoogleAuth.signupConfig), 12 | CredentialsProvider(CredentialAuth.signinConfig), 13 | CredentialsProvider(CredentialAuth.signupConfig), 14 | ], 15 | 16 | callbacks: { 17 | async signIn(ctx: any) { 18 | const { account } = ctx; 19 | 20 | if (GoogleAuth.canProvide(account.provider)) { 21 | return GoogleAuth.handle(ctx); 22 | } else if (CredentialAuth.canProvide(account.provider)) { 23 | return CredentialAuth.handle(ctx); 24 | } 25 | 26 | return "/auth/error"; 27 | }, 28 | async redirect(ctx: any) { 29 | const { url, baseUrl } = ctx; 30 | if (url.startsWith("/")) { 31 | return `${baseUrl}${url}`; 32 | } else if (new URL(url).origin === baseUrl) { 33 | return url; 34 | } 35 | return baseUrl; 36 | }, 37 | 38 | async jwt(ctx: any) { 39 | const { account, token } = ctx; 40 | 41 | if (account) { 42 | if (GoogleAuth.canProvide(account.provider)) { 43 | const profile = await GoogleAuth.getProfile(ctx); 44 | return profile; 45 | } 46 | if (CredentialAuth.canProvide(account.provider)) { 47 | const profile = await CredentialAuth.getProfile(ctx); 48 | return profile; 49 | } 50 | throw new Error("no authentication provider found"); 51 | } 52 | 53 | // client side access token validate from server side each time 54 | // skip if client side has stable auth session time 55 | // return the `token` object only 56 | const profile = await BackendService.getProfile(token.accessToken); 57 | return { 58 | ...profile, 59 | accessToken: token.accessToken, 60 | }; 61 | }, 62 | 63 | async session(ctx: any) { 64 | const { session, token } = ctx; 65 | session.user.id = token.id; 66 | session.user.name = token.name; 67 | session.user.email = token.email; 68 | session.user.phone = token.phone; 69 | session.user.isEmailVerified = token.isEmailVerified; 70 | 71 | return session; 72 | }, 73 | }, 74 | 75 | // Define custom pages for 76 | pages: { 77 | signIn: "/auth/login", 78 | error: "/auth/error", 79 | }, 80 | }; 81 | 82 | export default NextAuth(authOption); 83 | -------------------------------------------------------------------------------- /next-web/pages/auth/email-verify.tsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { Layout } from "components/Layout"; 3 | import { GetServerSideProps, NextPage } from "next"; 4 | 5 | const EmailVerifyPage: NextPage = (props: any) => { 6 | const { valid, user } = props; 7 | 8 | if (!valid) { 9 | return ( 10 | 11 |
12 |
13 |
14 |
15 |
16 | Verification failed 17 |
18 |
19 |
20 |
21 |
22 |
23 | ); 24 | } 25 | 26 | return ( 27 | 28 |
29 |
30 |
31 |
32 |

Email Verified!

33 |

34 | Hello {user.name} your email {user.email} is verified. 35 |

36 |
37 |
38 |
39 |
40 |
41 | ); 42 | }; 43 | 44 | export const getServerSideProps: GetServerSideProps = async (context) => { 45 | const { token } = context.query; 46 | if (!token) { 47 | return { 48 | props: { 49 | valid: false, 50 | }, 51 | }; 52 | } 53 | 54 | try { 55 | let res = await axios.post( 56 | `${process.env.BACKEND_API_HOST}/email-verify`, 57 | JSON.stringify({ token: token }) 58 | ); 59 | return { 60 | props: { 61 | valid: true, 62 | user: res.data, 63 | }, 64 | }; 65 | } catch (err) { 66 | return { 67 | props: { valid: false }, 68 | }; 69 | } 70 | }; 71 | 72 | export default EmailVerifyPage; 73 | -------------------------------------------------------------------------------- /next-web/pages/auth/error.tsx: -------------------------------------------------------------------------------- 1 | import { NextPage } from "next"; 2 | 3 | const ErrorPage: NextPage = (props: any) => { 4 | return ( 5 |
6 |

Authentication process can't proceed

7 |
8 | ); 9 | }; 10 | 11 | export default ErrorPage; 12 | -------------------------------------------------------------------------------- /next-web/pages/auth/login.tsx: -------------------------------------------------------------------------------- 1 | import { NextPage } from "next"; 2 | import { signIn } from "next-auth/react"; 3 | import Head from "next/head"; 4 | import Link from "next/link"; 5 | import { useRouter } from "next/router"; 6 | import { LoginForm } from "components/LoginForm"; 7 | 8 | const Login: NextPage = (props: any) => { 9 | const router = useRouter(); 10 | const { error, callbackUrl } = router.query; 11 | 12 | const getLoginSuccessRedirectUrl = () => { 13 | if (callbackUrl && typeof callbackUrl === "string") { 14 | return callbackUrl; 15 | } 16 | return "/profile"; 17 | }; 18 | 19 | const handleGoogleLogin = async () => { 20 | await signIn("google-signin", { 21 | callbackUrl: getLoginSuccessRedirectUrl(), 22 | redirect: false, 23 | }); 24 | }; 25 | 26 | const handleCredentialLogin = async (data: any) => { 27 | const res = await signIn("credentials-signin", { 28 | ...data, 29 | callbackUrl: getLoginSuccessRedirectUrl(), 30 | redirect: false, 31 | }); 32 | 33 | if (res) { 34 | if (res.ok) { 35 | router.push(res.url!); 36 | } else { 37 | router.push(`/auth/login?error=${res.error}`, undefined, { 38 | shallow: true, 39 | }); 40 | } 41 | } 42 | }; 43 | 44 | return ( 45 |
46 | 47 | Login 48 | 49 |
50 |
51 |
52 |
53 |
54 |
Login
55 | {error && ( 56 |
57 | {error} 58 |
59 | )} 60 | 61 |

Or

62 | 73 | 74 |
75 |

76 | No account? Please Signup{" "} 77 | here 78 |

79 |
80 |
81 |
82 |
83 |
84 |
85 | ); 86 | }; 87 | 88 | export default Login; 89 | -------------------------------------------------------------------------------- /next-web/pages/auth/signup.tsx: -------------------------------------------------------------------------------- 1 | import { NextPage } from "next"; 2 | import { signIn } from "next-auth/react"; 3 | import Head from "next/head"; 4 | import Link from "next/link"; 5 | import { useRouter } from "next/router"; 6 | import { SignupForm } from "components/SignupForm"; 7 | 8 | const SignupPage: NextPage = (props: any) => { 9 | const router = useRouter(); 10 | const { error } = router.query; 11 | 12 | const handleGoogleSignup = async () => { 13 | await signIn("google-signup", { 14 | callbackUrl: "/profile", 15 | redirect: false, 16 | }); 17 | }; 18 | 19 | const handleCredentialSignup = async (data: any) => { 20 | const res = await signIn("credentials-signup", { 21 | ...data, 22 | callbackUrl: "/profile", 23 | redirect: false, 24 | }); 25 | 26 | if (res) { 27 | if (res.ok) { 28 | router.push(res.url!); 29 | } else { 30 | router.push(`/auth/signup?error=${res.error}`, undefined, { 31 | shallow: true, 32 | }); 33 | } 34 | } 35 | }; 36 | 37 | return ( 38 |
39 | 40 | Signup 41 | 42 |
43 |
44 |
45 |
46 |
47 |
Registration
48 | {error && ( 49 |
50 | {error} 51 |
52 | )} 53 | 54 |

Or

55 | 66 |
67 |

68 | Already have account? Login{" "} 69 | here 70 |

71 |
72 |
73 |
74 |
75 |
76 |
77 | ); 78 | }; 79 | 80 | export default SignupPage; 81 | -------------------------------------------------------------------------------- /next-web/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { Layout } from "components/Layout"; 2 | 3 | export default function Home() { 4 | return ( 5 | 6 |

Go Next Custom Auth

7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /next-web/pages/profile.tsx: -------------------------------------------------------------------------------- 1 | import { Layout } from "components/Layout"; 2 | import { useSession } from "next-auth/react"; 3 | 4 | const ProfilePage = (props: any) => { 5 | const { data } = useSession(); 6 | 7 | return ( 8 | 9 |
10 |
11 |
12 |
    13 |
  • 14 |
    15 |
    Name
    16 | {data?.user?.name} 17 |
    18 |
  • 19 |
  • 20 |
    21 |
    Email
    22 | {data?.user?.email} 23 |
    24 | 25 | {/* @ts-ignore */} 26 | {data?.user?.isEmailVerified ? ( 27 | 28 | Verified 29 | 30 | ) : ( 31 | 32 | Not Verified 33 | 34 | )} 35 |
  • 36 |
  • 37 |
    38 |
    Phone
    39 | {/* @ts-ignore */} 40 | {data?.user?.phone ? data?.user?.phone : "N/A"} 41 |
    42 |
  • 43 |
44 |
45 |
46 |
47 |
48 | ); 49 | }; 50 | 51 | ProfilePage.auth = true; 52 | 53 | export default ProfilePage; 54 | -------------------------------------------------------------------------------- /next-web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafiulgits/go-next-authentication/2c720d83fb6b4cfdfd0909c5e658c86da0576d62/next-web/public/favicon.ico -------------------------------------------------------------------------------- /next-web/public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /next-web/services/backend.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { CredentialLoginDto, CredentialSignupDto, OAuthDto } from "dtos/auth"; 3 | 4 | const throwAxiosError = (err: any) => { 5 | if (!!err.response) { 6 | if (!!err.response.data && !!err.response.data["message"]) { 7 | throw new Error(err.response.data["message"]); 8 | } 9 | throw new Error(err.message); 10 | } 11 | throw new Error("failed to perform request"); 12 | }; 13 | 14 | const BackendService = { 15 | loginOAuth: async (data: OAuthDto) => { 16 | try { 17 | const res = await axios.post( 18 | `${process.env.BACKEND_API_HOST}/login/oauth`, 19 | JSON.stringify(data) 20 | ); 21 | return res.data; 22 | } catch (err: any) { 23 | throwAxiosError(err); 24 | } 25 | }, 26 | 27 | loginCredential: async (data: CredentialLoginDto) => { 28 | try { 29 | const res = await axios.post( 30 | `${process.env.BACKEND_API_HOST}/login/credential`, 31 | JSON.stringify(data) 32 | ); 33 | return res.data; 34 | } catch (err: any) { 35 | throwAxiosError(err); 36 | } 37 | }, 38 | 39 | signupOAuth: async (data: OAuthDto) => { 40 | try { 41 | const res = await axios.post( 42 | `${process.env.BACKEND_API_HOST}/signup/oauth`, 43 | JSON.stringify(data) 44 | ); 45 | return res.data; 46 | } catch (err: any) { 47 | throwAxiosError(err); 48 | } 49 | }, 50 | 51 | signupCredential: async (data: CredentialSignupDto) => { 52 | try { 53 | const res = await axios.post( 54 | `${process.env.BACKEND_API_HOST}/signup/credential`, 55 | JSON.stringify(data) 56 | ); 57 | return res.data; 58 | } catch (err: any) { 59 | throwAxiosError(err); 60 | } 61 | }, 62 | 63 | getProfile: async (accessToken: string) => { 64 | try { 65 | const res = await axios.get(`${process.env.BACKEND_API_HOST}/profile`, { 66 | headers: { 67 | Authorization: `Bearer ${accessToken}`, 68 | }, 69 | }); 70 | return res.data; 71 | } catch (err: any) { 72 | throwAxiosError(err); 73 | } 74 | }, 75 | }; 76 | 77 | export default BackendService; 78 | -------------------------------------------------------------------------------- /next-web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "baseUrl": "./", 18 | "paths": { 19 | "@pages": ["./pages"], 20 | "@components": ["./components"], 21 | "@services": ["./services"], 22 | "@util": ["./util"], 23 | "@auth": ["./auth"], 24 | "@dtos": ["./dtos"] 25 | } 26 | }, 27 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 28 | "exclude": ["node_modules"] 29 | } 30 | -------------------------------------------------------------------------------- /next-web/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/runtime@^7.16.3": 6 | version "7.20.0" 7 | resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.0.tgz#824a9ef325ffde6f78056059db3168c08785e24a" 8 | integrity sha512-NDYdls71fTXoU8TZHfbBWg7DiZfNzClcKui/+kyi6ppD2L1qnWW3VV6CjtaBXSUGGhiTWJ6ereOIkUvenif66Q== 9 | dependencies: 10 | regenerator-runtime "^0.13.10" 11 | 12 | "@next/env@13.0.1": 13 | version "13.0.1" 14 | resolved "https://registry.yarnpkg.com/@next/env/-/env-13.0.1.tgz#0361e203c7bfbc7b69679ec48f7b45a8f4cb1c2c" 15 | integrity sha512-gK60YoFae3s8qi5UgIzbvxOhsh5gKyEaiKH5+kLBUYXLlrPyWJR2xKBj2WqvHkO7wDX7/Hed3DAqjSpU4ijIvQ== 16 | 17 | "@next/swc-android-arm-eabi@13.0.1": 18 | version "13.0.1" 19 | resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.1.tgz#7ce2a7b6576845bc6d7f55504bf9b82a0d9a2792" 20 | integrity sha512-M28QSbohZlNXNn//HY6lV2T3YaMzG58Jwr0YwOdVmOQv6i+7lu6xe3GqQu4kdqInqhLrBXnL+nabFuGTVSHtTg== 21 | 22 | "@next/swc-android-arm64@13.0.1": 23 | version "13.0.1" 24 | resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.0.1.tgz#85a13d7667042394939741be218076e4e83a45a2" 25 | integrity sha512-szmO/i6GoHcPXcbhUKhwBMETWHNXH3ITz9wfxwOOFBNKdDU8pjKsHL88lg28aOiQYZSU1sxu1v1p9KY5kJIZCg== 26 | 27 | "@next/swc-darwin-arm64@13.0.1": 28 | version "13.0.1" 29 | resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.1.tgz#d615d286127bb096a8950a9d7180fcc5d307614d" 30 | integrity sha512-O1RxCaiDNOjGZmdAp6SQoHUITt9aVDQXoR3lZ/TloI/NKRAyAV4u0KUUofK+KaZeHOmVTnPUaQuCyZSc3i1x5Q== 31 | 32 | "@next/swc-darwin-x64@13.0.1": 33 | version "13.0.1" 34 | resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.1.tgz#f410beb8cbe0e82562226309f8ec8924cc6cb410" 35 | integrity sha512-8E6BY/VO+QqQkthhoWgB8mJMw1NcN9Vhl2OwEwxv8jy2r3zjeU+WNRxz4y8RLbcY0R1h+vHlXuP0mLnuac84tQ== 36 | 37 | "@next/swc-freebsd-x64@13.0.1": 38 | version "13.0.1" 39 | resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.1.tgz#16eb9652d3f638305ca16b558408f5bc5eb6edde" 40 | integrity sha512-ocwoOxm2KVwF50RyoAT+2RQPLlkyoF7sAqzMUVgj+S6+DTkY3iwH+Zpo0XAk2pnqT9qguOrKnEpq9EIx//+K7Q== 41 | 42 | "@next/swc-linux-arm-gnueabihf@13.0.1": 43 | version "13.0.1" 44 | resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.1.tgz#0da0700ccf654f813b4c86d057a998598a2fd427" 45 | integrity sha512-yO7e3zITfGol/N6lPQnmIRi0WyuILBMXrvH6EdmWzzqMDJFfTCII6l+B6gMO5WVDCTQUGQlQRNZ7sFqWR4I71g== 46 | 47 | "@next/swc-linux-arm64-gnu@13.0.1": 48 | version "13.0.1" 49 | resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.1.tgz#f34759cd41086f5b8b582081b2af54f67dc544ae" 50 | integrity sha512-OEs6WDPDI8RyM8SjOqTDMqMBfOlU97VnW6ZMXUvzUTyH0K9c7NF+cn7UMu+I4tKFN0uJ9WQs/6TYaFBGkgoVVA== 51 | 52 | "@next/swc-linux-arm64-musl@13.0.1": 53 | version "13.0.1" 54 | resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.1.tgz#bcfbf1cdfb9f4d632e7ebd67fd62b768cdd08cb7" 55 | integrity sha512-y5ypFK0Y3urZSFoQxbtDqvKsBx026sz+Fm+xHlPWlGHNZrbs3Q812iONjcZTo09QwRMk5X86iMWBRxV18xMhaw== 56 | 57 | "@next/swc-linux-x64-gnu@13.0.1": 58 | version "13.0.1" 59 | resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.1.tgz#ed77528d4a3195d5e57d5d94d12cb2206c2b19ac" 60 | integrity sha512-XDIHEE6SU8VCF+dUVntD6PDv6RK31N0forx9kucZBYirbe8vCZ+Yx8hYgvtIaGrTcWtGxibxmND0pIuHDq8H5g== 61 | 62 | "@next/swc-linux-x64-musl@13.0.1": 63 | version "13.0.1" 64 | resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.1.tgz#74cda49229d2a7fa421fee6b7dcd621a57934a5e" 65 | integrity sha512-yxIOuuz5EOx0F1FDtsyzaLgnDym0Ysxv8CWeJyDTKKmt9BVyITg6q/cD+RP9bEkT1TQi+PYXIMATSz675Q82xw== 66 | 67 | "@next/swc-win32-arm64-msvc@13.0.1": 68 | version "13.0.1" 69 | resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.1.tgz#15d6add92aa897148d6c45749bf9d2eacee87197" 70 | integrity sha512-+ucLe2qgQzP+FM94jD4ns6LDGyMFaX9k3lVHqu/tsQCy2giMymbport4y4p77mYcXEMlDaHMzlHgOQyHRniWFA== 71 | 72 | "@next/swc-win32-ia32-msvc@13.0.1": 73 | version "13.0.1" 74 | resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.1.tgz#e0c57902fe75327d092abb1ef19657775fe26f85" 75 | integrity sha512-Krr/qGN7OB35oZuvMAZKoXDt2IapynIWLh5A5rz6AODb7f/ZJqyAuZSK12vOa2zKdobS36Qm4IlxxBqn9c00MA== 76 | 77 | "@next/swc-win32-x64-msvc@13.0.1": 78 | version "13.0.1" 79 | resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.1.tgz#469dde61519f6a310874af93ee5969f1d5ff6d03" 80 | integrity sha512-t/0G33t/6VGWZUGCOT7rG42qqvf/x+MrFp1CU+8CN6PrjSSL57R5bqkXfubV9t4eCEnUxVP+5Hn3MoEXEebtEw== 81 | 82 | "@panva/hkdf@^1.0.1": 83 | version "1.0.2" 84 | resolved "https://registry.yarnpkg.com/@panva/hkdf/-/hkdf-1.0.2.tgz#bab0f09d09de9fd83628220d496627681bc440d6" 85 | integrity sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA== 86 | 87 | "@swc/helpers@0.4.11": 88 | version "0.4.11" 89 | resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.11.tgz#db23a376761b3d31c26502122f349a21b592c8de" 90 | integrity sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw== 91 | dependencies: 92 | tslib "^2.4.0" 93 | 94 | "@types/node@18.11.8": 95 | version "18.11.8" 96 | resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.8.tgz#16d222a58d4363a2a359656dd20b28414de5d265" 97 | integrity sha512-uGwPWlE0Hj972KkHtCDVwZ8O39GmyjfMane1Z3GUBGGnkZ2USDq7SxLpVIiIHpweY9DS0QTDH0Nw7RNBsAAZ5A== 98 | 99 | "@types/prop-types@*": 100 | version "15.7.5" 101 | resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" 102 | integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== 103 | 104 | "@types/react-dom@18.0.8": 105 | version "18.0.8" 106 | resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.8.tgz#d2606d855186cd42cc1b11e63a71c39525441685" 107 | integrity sha512-C3GYO0HLaOkk9dDAz3Dl4sbe4AKUGTCfFIZsz3n/82dPNN8Du533HzKatDxeUYWu24wJgMP1xICqkWk1YOLOIw== 108 | dependencies: 109 | "@types/react" "*" 110 | 111 | "@types/react@*", "@types/react@18.0.24": 112 | version "18.0.24" 113 | resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.24.tgz#2f79ed5b27f08d05107aab45c17919754cc44c20" 114 | integrity sha512-wRJWT6ouziGUy+9uX0aW4YOJxAY0bG6/AOk5AW5QSvZqI7dk6VBIbXvcVgIw/W5Jrl24f77df98GEKTJGOLx7Q== 115 | dependencies: 116 | "@types/prop-types" "*" 117 | "@types/scheduler" "*" 118 | csstype "^3.0.2" 119 | 120 | "@types/scheduler@*": 121 | version "0.16.2" 122 | resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" 123 | integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== 124 | 125 | asynckit@^0.4.0: 126 | version "0.4.0" 127 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 128 | integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== 129 | 130 | axios@^1.1.3: 131 | version "1.1.3" 132 | resolved "https://registry.yarnpkg.com/axios/-/axios-1.1.3.tgz#8274250dada2edf53814ed7db644b9c2866c1e35" 133 | integrity sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA== 134 | dependencies: 135 | follow-redirects "^1.15.0" 136 | form-data "^4.0.0" 137 | proxy-from-env "^1.1.0" 138 | 139 | bootstrap@5.2.2: 140 | version "5.2.2" 141 | resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.2.2.tgz#834e053eed584a65e244d8aa112a6959f56e27a0" 142 | integrity sha512-dEtzMTV71n6Fhmbg4fYJzQsw1N29hJKO1js5ackCgIpDcGid2ETMGC6zwSYw09v05Y+oRdQ9loC54zB1La3hHQ== 143 | 144 | caniuse-lite@^1.0.30001406: 145 | version "1.0.30001427" 146 | resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001427.tgz#d3a749f74be7ae0671fbec3a4eea18576e8ad646" 147 | integrity sha512-lfXQ73oB9c8DP5Suxaszm+Ta2sr/4tf8+381GkIm1MLj/YdLf+rEDyDSRCzeltuyTVGm+/s18gdZ0q+Wmp8VsQ== 148 | 149 | client-only@0.0.1: 150 | version "0.0.1" 151 | resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" 152 | integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== 153 | 154 | combined-stream@^1.0.8: 155 | version "1.0.8" 156 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" 157 | integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== 158 | dependencies: 159 | delayed-stream "~1.0.0" 160 | 161 | cookie@^0.5.0: 162 | version "0.5.0" 163 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" 164 | integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== 165 | 166 | csstype@^3.0.2: 167 | version "3.1.1" 168 | resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" 169 | integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== 170 | 171 | delayed-stream@~1.0.0: 172 | version "1.0.0" 173 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 174 | integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== 175 | 176 | follow-redirects@^1.15.0: 177 | version "1.15.2" 178 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" 179 | integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== 180 | 181 | form-data@^4.0.0: 182 | version "4.0.0" 183 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" 184 | integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== 185 | dependencies: 186 | asynckit "^0.4.0" 187 | combined-stream "^1.0.8" 188 | mime-types "^2.1.12" 189 | 190 | jose@^4.10.0, jose@^4.9.3: 191 | version "4.10.4" 192 | resolved "https://registry.yarnpkg.com/jose/-/jose-4.10.4.tgz#5f934b2fcf2995776e8f671f7523c6ac52c138f7" 193 | integrity sha512-eBH77Xs9Yc/oTDvukhAEDVMijhekPuNktXJL4tUlB22jqKP1k48v5nmsUmc8feoJPsxB3HsfEt2LbVSoz+1mng== 194 | 195 | "js-tokens@^3.0.0 || ^4.0.0": 196 | version "4.0.0" 197 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 198 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 199 | 200 | loose-envify@^1.1.0, loose-envify@^1.4.0: 201 | version "1.4.0" 202 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" 203 | integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== 204 | dependencies: 205 | js-tokens "^3.0.0 || ^4.0.0" 206 | 207 | lru-cache@^6.0.0: 208 | version "6.0.0" 209 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" 210 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 211 | dependencies: 212 | yallist "^4.0.0" 213 | 214 | mime-db@1.52.0: 215 | version "1.52.0" 216 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" 217 | integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== 218 | 219 | mime-types@^2.1.12: 220 | version "2.1.35" 221 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" 222 | integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== 223 | dependencies: 224 | mime-db "1.52.0" 225 | 226 | nanoid@^3.3.4: 227 | version "3.3.4" 228 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" 229 | integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== 230 | 231 | next-auth@^4.15.0: 232 | version "4.15.0" 233 | resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.15.0.tgz#1e350258b240cff7e09e81f066e26ad8fe540c85" 234 | integrity sha512-IasNzGLM2VlmyioDdZaRwBBBm8b5xo+zbbqVWHFh0bY6iQUZ3vuudrsdHNdxkXV3LSHdKNaoWEpYr4BydB7mCw== 235 | dependencies: 236 | "@babel/runtime" "^7.16.3" 237 | "@panva/hkdf" "^1.0.1" 238 | cookie "^0.5.0" 239 | jose "^4.9.3" 240 | oauth "^0.9.15" 241 | openid-client "^5.1.0" 242 | preact "^10.6.3" 243 | preact-render-to-string "^5.1.19" 244 | uuid "^8.3.2" 245 | 246 | next@13.0.1: 247 | version "13.0.1" 248 | resolved "https://registry.yarnpkg.com/next/-/next-13.0.1.tgz#8b4fc9998e58f503bdecb92f06fe6f850ac260d0" 249 | integrity sha512-ErCNBPIeZMKFn6hX+ZBSlqZVgJIeitEqhGTuQUNmYXJ07/A71DZ7AJI8eyHYUdBb686LUpV1/oBdTq9RpzRVPg== 250 | dependencies: 251 | "@next/env" "13.0.1" 252 | "@swc/helpers" "0.4.11" 253 | caniuse-lite "^1.0.30001406" 254 | postcss "8.4.14" 255 | styled-jsx "5.1.0" 256 | use-sync-external-store "1.2.0" 257 | optionalDependencies: 258 | "@next/swc-android-arm-eabi" "13.0.1" 259 | "@next/swc-android-arm64" "13.0.1" 260 | "@next/swc-darwin-arm64" "13.0.1" 261 | "@next/swc-darwin-x64" "13.0.1" 262 | "@next/swc-freebsd-x64" "13.0.1" 263 | "@next/swc-linux-arm-gnueabihf" "13.0.1" 264 | "@next/swc-linux-arm64-gnu" "13.0.1" 265 | "@next/swc-linux-arm64-musl" "13.0.1" 266 | "@next/swc-linux-x64-gnu" "13.0.1" 267 | "@next/swc-linux-x64-musl" "13.0.1" 268 | "@next/swc-win32-arm64-msvc" "13.0.1" 269 | "@next/swc-win32-ia32-msvc" "13.0.1" 270 | "@next/swc-win32-x64-msvc" "13.0.1" 271 | 272 | oauth@^0.9.15: 273 | version "0.9.15" 274 | resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" 275 | integrity sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA== 276 | 277 | object-assign@^4.1.1: 278 | version "4.1.1" 279 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 280 | integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== 281 | 282 | object-hash@^2.0.1: 283 | version "2.2.0" 284 | resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" 285 | integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== 286 | 287 | oidc-token-hash@^5.0.1: 288 | version "5.0.1" 289 | resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz#ae6beec3ec20f0fd885e5400d175191d6e2f10c6" 290 | integrity sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ== 291 | 292 | openid-client@^5.1.0: 293 | version "5.2.1" 294 | resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.2.1.tgz#dd26298aca237625298ef34ff11ad9276917df28" 295 | integrity sha512-KPxqWnxobG/70Cxqyvd43RWfCfHedFnCdHSBpw5f7WnTnuBAeBnvot/BIo+brrcTr0wyAYUlL/qejQSGwWtdIg== 296 | dependencies: 297 | jose "^4.10.0" 298 | lru-cache "^6.0.0" 299 | object-hash "^2.0.1" 300 | oidc-token-hash "^5.0.1" 301 | 302 | picocolors@^1.0.0: 303 | version "1.0.0" 304 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" 305 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== 306 | 307 | postcss@8.4.14: 308 | version "8.4.14" 309 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" 310 | integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== 311 | dependencies: 312 | nanoid "^3.3.4" 313 | picocolors "^1.0.0" 314 | source-map-js "^1.0.2" 315 | 316 | preact-render-to-string@^5.1.19: 317 | version "5.2.6" 318 | resolved "https://registry.yarnpkg.com/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz#0ff0c86cd118d30affb825193f18e92bd59d0604" 319 | integrity sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw== 320 | dependencies: 321 | pretty-format "^3.8.0" 322 | 323 | preact@^10.6.3: 324 | version "10.11.2" 325 | resolved "https://registry.yarnpkg.com/preact/-/preact-10.11.2.tgz#e43f2a2f2985dedb426bb4c765b7bb037734f8a8" 326 | integrity sha512-skAwGDFmgxhq1DCBHke/9e12ewkhc7WYwjuhHB8HHS8zkdtITXLRmUMTeol2ldxvLwYtwbFeifZ9uDDWuyL4Iw== 327 | 328 | pretty-format@^3.8.0: 329 | version "3.8.0" 330 | resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-3.8.0.tgz#bfbed56d5e9a776645f4b1ff7aa1a3ac4fa3c385" 331 | integrity sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew== 332 | 333 | prop-types@^15.6.0: 334 | version "15.8.1" 335 | resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" 336 | integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== 337 | dependencies: 338 | loose-envify "^1.4.0" 339 | object-assign "^4.1.1" 340 | react-is "^16.13.1" 341 | 342 | proxy-from-env@^1.1.0: 343 | version "1.1.0" 344 | resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" 345 | integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== 346 | 347 | react-dom@18.2.0: 348 | version "18.2.0" 349 | resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" 350 | integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== 351 | dependencies: 352 | loose-envify "^1.1.0" 353 | scheduler "^0.23.0" 354 | 355 | react-google-login@^5.2.2: 356 | version "5.2.2" 357 | resolved "https://registry.yarnpkg.com/react-google-login/-/react-google-login-5.2.2.tgz#a20b46440c6c1610175ef75baf427118ff0e9859" 358 | integrity sha512-JUngfvaSMcOuV0lFff7+SzJ2qviuNMQdqlsDJkUM145xkGPVIfqWXq9Ui+2Dr6jdJWH5KYdynz9+4CzKjI5u6g== 359 | dependencies: 360 | "@types/react" "*" 361 | prop-types "^15.6.0" 362 | 363 | react-is@^16.13.1: 364 | version "16.13.1" 365 | resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" 366 | integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== 367 | 368 | react@18.2.0: 369 | version "18.2.0" 370 | resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" 371 | integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== 372 | dependencies: 373 | loose-envify "^1.1.0" 374 | 375 | regenerator-runtime@^0.13.10: 376 | version "0.13.10" 377 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz#ed07b19616bcbec5da6274ebc75ae95634bfc2ee" 378 | integrity sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw== 379 | 380 | scheduler@^0.23.0: 381 | version "0.23.0" 382 | resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" 383 | integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== 384 | dependencies: 385 | loose-envify "^1.1.0" 386 | 387 | source-map-js@^1.0.2: 388 | version "1.0.2" 389 | resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" 390 | integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== 391 | 392 | styled-jsx@5.1.0: 393 | version "5.1.0" 394 | resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.0.tgz#4a5622ab9714bd3fcfaeec292aa555871f057563" 395 | integrity sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ== 396 | dependencies: 397 | client-only "0.0.1" 398 | 399 | tslib@^2.4.0: 400 | version "2.4.1" 401 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" 402 | integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== 403 | 404 | typescript@4.8.4: 405 | version "4.8.4" 406 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" 407 | integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== 408 | 409 | use-sync-external-store@1.2.0: 410 | version "1.2.0" 411 | resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" 412 | integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== 413 | 414 | uuid@^8.3.2: 415 | version "8.3.2" 416 | resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" 417 | integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== 418 | 419 | yallist@^4.0.0: 420 | version "4.0.0" 421 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 422 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 423 | --------------------------------------------------------------------------------