feat: login wall (#42)

* feat: add login-wall app

* fix: rename vars

* fix: rename vars

* update docker file

* add validations for app state

* add host check for app

* fix: docker file
This commit is contained in:
Lakhan Samani 2021-08-04 12:18:57 +05:30 committed by GitHub
parent d1973c1f8f
commit f88363e6dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 2274 additions and 120 deletions

View File

@ -3,4 +3,6 @@ server/server
.git
.gitignore
README.md
ROADMAP.md
ROADMAP.md
build
.env

5
.gitignore vendored
View File

@ -1,3 +1,6 @@
server/server
server/.env
data
data
app/node_modules
build
.env

View File

@ -1,19 +1,20 @@
FROM golang:1.16-alpine as builder
WORKDIR /app
COPY . .
COPY server server
COPY Makefile .
ARG VERSION=0.1.0-beta.0
ENV VERSION="${VERSION}"
RUN apk add build-base &&\
cd server && \
go mod download && \
make clean && make && \
chmod 777 server
chmod 777 build/server
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/server/server .
COPY app app
COPY templates templates
COPY --from=builder /app/build build
EXPOSE 8080
CMD [ "./server" ]
CMD [ "./build/server" ]

7
Makefile Normal file
View File

@ -0,0 +1,7 @@
DEFAULT_VERSION=0.1.0-local
VERSION := $(or $(VERSION),$(DEFAULT_VERSION))
cmd:
cd server && go build -ldflags "-w -X main.Version=$(VERSION)" -o '../build/server'
clean:
rm -rf build

View File

@ -1,2 +0,0 @@
- [] Add env to disalbe email verification -> this can be helpful while onboarding user
- []

3
app/README.md Normal file
View File

@ -0,0 +1,3 @@
# Authorizer APP
App that can be used as login wall for your any application in combination with @authorizerdev/@authorizer.js

16
app/build/bundle.css Normal file
View File

@ -0,0 +1,16 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #374151;
font-size: 14px;
}
*,
*:before,
*:after {
box-sizing: inherit;
}

275
app/build/bundle.js Normal file

File diff suppressed because one or more lines are too long

7
app/build/bundle.js.map Normal file

File diff suppressed because one or more lines are too long

1402
app/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

25
app/package.json Normal file
View File

@ -0,0 +1,25 @@
{
"name": "app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "esbuild src/index.tsx --bundle --minify --sourcemap --outfile=build/bundle.js"
},
"keywords": [],
"author": "Lakhan Samani",
"license": "ISC",
"dependencies": {
"@authorizerdev/authorizer-react": "^0.1.0-beta.6",
"@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9",
"esbuild": "^0.12.17",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.0",
"typescript": "^4.3.5"
},
"devDependencies": {
"@types/react-router-dom": "^5.1.8"
}
}

33
app/src/App.tsx Normal file
View File

@ -0,0 +1,33 @@
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { AuthorizerProvider } from '@authorizerdev/authorizer-react';
import Root from './Root';
export default function App() {
// @ts-ignore
const globalState: Record<string, string> = window['__authorizer__'];
return (
<div style={{ display: 'flex', justifyContent: 'center' }}>
<div
style={{
width: 400,
margin: `10px auto`,
border: `1px solid #D1D5DB`,
padding: `25px 20px`,
borderRadius: 5,
}}
>
<BrowserRouter>
<AuthorizerProvider
config={{
authorizerURL: globalState.authorizerURL,
redirectURL: globalState.redirectURL,
}}
>
<Root />
</AuthorizerProvider>
</BrowserRouter>
</div>
</div>
);
}

35
app/src/Root.tsx Normal file
View File

@ -0,0 +1,35 @@
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import { useAuthorizer } from '@authorizerdev/authorizer-react';
import Dashboard from './pages/dashboard';
import Login from './pages/login';
import ResetPassword from './pages/rest-password';
export default function Root() {
const { token, loading } = useAuthorizer();
if (loading) {
return <h1>Loading...</h1>;
}
if (token) {
return (
<Switch>
<Route path="/app" exact>
<Dashboard />
</Route>
</Switch>
);
}
return (
<Switch>
<Route path="/app" exact>
<Login />
</Route>
<Route path="/app/reset-password">
<ResetPassword />
</Route>
</Switch>
);
}

5
app/src/index.tsx Normal file
View File

@ -0,0 +1,5 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));

View File

@ -0,0 +1,52 @@
import React from 'react';
import { useAuthorizer } from '@authorizerdev/authorizer-react';
export default function Dashboard() {
const [loading, setLoading] = React.useState(false);
const { user, setToken, graphQlRef } = useAuthorizer();
const onLogout = async () => {
setLoading(true);
await graphQlRef
.mutation(
`
mutation {
logout {
message
}
}
`
)
.toPromise();
setToken(null);
setLoading(false);
};
return (
<div>
<h1>Hey 👋,</h1>
<p>Thank you for joining authorizer demo app.</p>
<p>
Your email address is{' '}
<a href={`mailto:${user?.email}`} style={{ color: '#3B82F6' }}>
{user?.email}
</a>
</p>
<br />
{loading ? (
<h3>Processing....</h3>
) : (
<h3
style={{
color: '#3B82F6',
cursor: 'pointer',
}}
onClick={onLogout}
>
Logout
</h3>
)}
</div>
);
}

10
app/src/pages/login.tsx Normal file
View File

@ -0,0 +1,10 @@
import React, { Fragment } from 'react';
import { Authorizer } from '@authorizerdev/authorizer-react';
export default function Login() {
return (
<Fragment>
<Authorizer />
</Fragment>
);
}

View File

@ -0,0 +1,12 @@
import React, { Fragment } from 'react';
import { AuthorizerResetPassword } from '@authorizerdev/authorizer-react';
export default function ResetPassword() {
return (
<Fragment>
<h1 style={{ textAlign: 'center' }}>Reset Password</h1>
<br />
<AuthorizerResetPassword />
</Fragment>
);
}

72
app/tsconfig.json Normal file
View File

@ -0,0 +1,72 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": ["es2018", "dom"], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
"jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
"rootDir": "src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}

View File

@ -17,4 +17,4 @@ services:
depends_on:
- db
env_file:
- server/.env
- .env

View File

@ -1,7 +0,0 @@
DEFAULT_VERSION=0.1.0-local
VERSION := $(or $(VERSION),$(DEFAULT_VERSION))
cmd:
go build -ldflags "-w -X main.Version=$(VERSION)"
clean:
rm -rf server

View File

@ -12,8 +12,9 @@ var (
SENDER_PASSWORD = ""
JWT_TYPE = ""
JWT_SECRET = ""
FRONTEND_URL = ""
AUTHORIZER_DOMAIN = ""
ALLOWED_ORIGINS = []string{}
ALLOWED_CALLBACK_URLS = []string{}
AUTHORIZER_URL = ""
PORT = "8080"
REDIS_URL = ""
IS_PROD = false

View File

@ -17,7 +17,7 @@ var Version string
func ParseArgs() {
dbURL := flag.String("database_url", "", "Database connection string")
dbType := flag.String("databse_type", "", "Database type, possible values are postgres,mysql,sqlit")
authroizerDomain := flag.String("authorizer_domain", "", "Domain name for authorizer instance, eg: https://xyz.herokuapp.com")
authorizerURL := flag.String("AUTHORIZER_URL", "", "URL for authorizer instance, eg: https://xyz.herokuapp.com")
flag.Parse()
if *dbURL != "" {
constants.DATABASE_URL = *dbURL
@ -27,8 +27,8 @@ func ParseArgs() {
constants.DATABASE_TYPE = *dbType
}
if *authroizerDomain != "" {
constants.AUTHORIZER_DOMAIN = *authroizerDomain
if *authorizerURL != "" {
constants.AUTHORIZER_URL = *authorizerURL
}
}
@ -51,8 +51,7 @@ func InitEnv() {
constants.SENDER_PASSWORD = os.Getenv("SENDER_PASSWORD")
constants.JWT_SECRET = os.Getenv("JWT_SECRET")
constants.JWT_TYPE = os.Getenv("JWT_TYPE")
constants.FRONTEND_URL = strings.TrimSuffix(os.Getenv("FRONTEND_URL"), "/")
constants.AUTHORIZER_DOMAIN = strings.TrimSuffix(os.Getenv("AUTHORIZER_DOMAIN"), "/")
constants.AUTHORIZER_URL = strings.TrimSuffix(os.Getenv("AUTHORIZER_URL"), "/")
constants.PORT = os.Getenv("PORT")
constants.REDIS_URL = os.Getenv("REDIS_URL")
constants.COOKIE_NAME = os.Getenv("COOKIE_NAME")
@ -84,6 +83,32 @@ func InitEnv() {
constants.IS_PROD = false
}
allowedOriginsSplit := strings.Split(os.Getenv("ALLOWED_ORIGINS"), ",")
allowedOrigins := []string{}
for _, val := range allowedOriginsSplit {
trimVal := strings.TrimSpace(val)
if trimVal != "" {
allowedOrigins = append(allowedOrigins, trimVal)
}
}
if len(allowedOrigins) == 0 {
allowedOrigins = []string{"*"}
}
constants.ALLOWED_ORIGINS = allowedOrigins
allowedCallbackSplit := strings.Split(os.Getenv("ALLOWED_CALLBACK_URLS"), ",")
allowedCallbacks := []string{}
for _, val := range allowedCallbackSplit {
trimVal := strings.TrimSpace(val)
if trimVal != "" {
allowedCallbacks = append(allowedCallbacks, trimVal)
}
}
if len(allowedCallbackSplit) == 0 {
allowedCallbackSplit = []string{"*"}
}
constants.ALLOWED_CALLBACK_URLS = allowedCallbackSplit
ParseArgs()
if constants.DATABASE_URL == "" {
panic("Database url is required")
@ -101,10 +126,6 @@ func InitEnv() {
constants.COOKIE_NAME = "authorizer"
}
if constants.AUTHORIZER_DOMAIN == "" {
constants.AUTHORIZER_DOMAIN = "http://localhost:8080"
}
if constants.DISABLE_BASIC_AUTHENTICATION == "" {
constants.DISABLE_BASIC_AUTHENTICATION = "false"
}

View File

@ -5,19 +5,24 @@ go 1.16
require (
github.com/99designs/gqlgen v0.13.0
github.com/gin-gonic/gin v1.7.2
github.com/go-playground/validator/v10 v10.8.0 // indirect
github.com/go-redis/redis/v8 v8.11.0
github.com/golang-jwt/jwt v3.2.1+incompatible
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/google/uuid v1.3.0
github.com/jackc/pgproto3/v2 v2.1.0 // indirect
github.com/joho/godotenv v1.3.0
github.com/json-iterator/go v1.1.11 // indirect
github.com/mattn/go-isatty v0.0.13 // indirect
github.com/mattn/go-sqlite3 v1.14.7 // indirect
github.com/ugorji/go v1.2.6 // indirect
github.com/vektah/gqlparser/v2 v2.1.0
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gorm.io/driver/mysql v1.1.1
gorm.io/driver/postgres v1.1.0
gorm.io/driver/sqlite v1.1.4

View File

@ -68,7 +68,6 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
@ -88,19 +87,11 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dghubble/go-twitter v0.0.0-20190719072343-39e5462e111f/go.mod h1:xfg4uS5LEzOj8PgZV7SQYRHbG7jPUnelEiaAVJxmhJE=
github.com/dghubble/gologin v2.1.0+incompatible h1:LAk9a91axNP72AGTNp7afVigkdhakN7IyDGcceQ+R6g=
github.com/dghubble/gologin v2.1.0+incompatible/go.mod h1:+EjjX5AiOREcyqxhz0c6I8OsL+6F9/38WD1CDcClx+Y=
github.com/dghubble/gologin/v2 v2.3.0 h1:SMHahscgKmgrv4X+OAwFCJCuJ6mbLxOqB+FAVU+tOSA=
github.com/dghubble/gologin/v2 v2.3.0/go.mod h1:qGAUHuIYV0WP3kwoPjLhG+YIlGqy8O13YjItosCBKdo=
github.com/dghubble/oauth1 v0.6.0/go.mod h1:8pFdfPkv/jr8mkChVbNVuJ0suiHe278BtWI4Tk1ujxk=
github.com/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
@ -143,8 +134,9 @@ github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8c
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.8.0 h1:1kAa0fCrnpv+QYdkdcRzrRM7AyYs5o8+jZdJCz9xj6k=
github.com/go-playground/validator/v10 v10.8.0/go.mod h1:9JhgTzTaE31GZDpH/HSvHiRJrJ3iKAgqqH0Bl/Ocjdk=
github.com/go-redis/redis/v8 v8.11.0 h1:O1Td0mQ8UFChQ3N9zFQqo6kTU2cJ+/it88gDB+zg0wo=
github.com/go-redis/redis/v8 v8.11.0/go.mod h1:DLomh7y2e3ggQXQLd1YgmvIfecPJoFl7WU5SOQ/r06M=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
@ -201,8 +193,6 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@ -322,8 +312,9 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
@ -339,8 +330,9 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@ -362,8 +354,9 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA=
@ -453,7 +446,6 @@ github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
@ -465,7 +457,6 @@ github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9Nz
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY=
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@ -487,17 +478,18 @@ github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
github.com/vektah/gqlparser/v2 v2.1.0 h1:uiKJ+T5HMGGQM2kRKQ8Pxw8+Zq9qhhZhz/lieYvCMns=
@ -541,8 +533,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -572,7 +564,6 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -676,8 +667,9 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -741,7 +733,6 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -856,8 +847,11 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.1.1 h1:yr1bpyqiwuSPJ4aGGUX9nu46RHXlF8RASQVb1QQNcvo=
gorm.io/driver/mysql v1.1.1/go.mod h1:KdrTanmfLPPyAOeYGyG+UpDys7/7eeWT1zCq+oekYnU=
gorm.io/driver/postgres v1.1.0 h1:afBljg7PtJ5lA6YUWluV2+xovIPhS+YiInuL3kUjrbk=

87
server/handlers/app.go Normal file
View File

@ -0,0 +1,87 @@
package handlers
import (
"encoding/base64"
"encoding/json"
"log"
"net/http"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/gin-gonic/gin"
)
type State struct {
AuthorizerURL string `json:"authorizerURL"`
RedirectURL string `json:"redirectURL"`
}
func AppHandler() gin.HandlerFunc {
return func(c *gin.Context) {
host := "http://" + c.Request.Host
state := c.Query("state")
var stateObj State
if state == "" {
cookie, err := utils.GetAuthToken(c)
log.Println(`cookie`, cookie)
if err != nil {
c.JSON(400, gin.H{"error": "invalid state"})
return
}
stateObj.AuthorizerURL = host
stateObj.RedirectURL = host + "/app"
} else {
decodedState, err := base64.StdEncoding.DecodeString(state)
if err != nil {
c.JSON(400, gin.H{"error": "[unable to decode state] invalid state"})
return
}
err = json.Unmarshal(decodedState, &stateObj)
if err != nil {
c.JSON(400, gin.H{"error": "[unable to parse state] invalid state"})
return
}
// validate redirect url with allowed origins
if !utils.IsValidRedirectURL(stateObj.RedirectURL) {
c.JSON(400, gin.H{"error": "invalid redirect url"})
return
}
if stateObj.AuthorizerURL == "" {
c.JSON(400, gin.H{"error": "invalid authorizer url"})
return
}
// validate host and domain of authorizer url
if utils.GetDomainName(stateObj.AuthorizerURL) != utils.GetDomainName(host) {
c.JSON(400, gin.H{"error": "invalid host url"})
return
}
}
log.Println(gin.H{
"data": map[string]string{
"authorizerURL": "http://" + stateObj.AuthorizerURL,
"redirectURL": stateObj.RedirectURL,
},
})
// debug the request state
if pusher := c.Writer.Pusher(); pusher != nil {
// use pusher.Push() to do server push
if err := pusher.Push("/app/build/bundle.js", nil); err != nil {
log.Printf("Failed to push: %v", err)
}
}
c.HTML(http.StatusOK, "app.tmpl", gin.H{
"data": map[string]string{
"authorizerURL": stateObj.AuthorizerURL,
"redirectURL": stateObj.RedirectURL,
},
})
}
}

View File

@ -15,8 +15,8 @@ func GraphqlHandler() gin.HandlerFunc {
h := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}}))
return func(c *gin.Context) {
if constants.AUTHORIZER_DOMAIN == "" {
constants.AUTHORIZER_DOMAIN = "https://" + c.Request.Host
if constants.AUTHORIZER_URL == "" {
constants.AUTHORIZER_URL = "https://" + c.Request.Host
}
h.ServeHTTP(c.Writer, c.Request)
}

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
"time"
@ -18,12 +19,7 @@ import (
"golang.org/x/oauth2"
)
func processGoogleUserInfo(state string, code string, c *gin.Context) error {
sessionState := session.GetToken(state)
if sessionState == "" {
return fmt.Errorf("invalid oauth state")
}
session.DeleteToken(sessionState)
func processGoogleUserInfo(code string, c *gin.Context) error {
token, err := oauth.OAuthProvider.GoogleConfig.Exchange(oauth2.NoContext, code)
if err != nil {
return fmt.Errorf("invalid google exchange code: %s", err.Error())
@ -82,12 +78,7 @@ func processGoogleUserInfo(state string, code string, c *gin.Context) error {
return nil
}
func processGithubUserInfo(state string, code string, c *gin.Context) error {
sessionState := session.GetToken(state)
if sessionState == "" {
return fmt.Errorf("invalid oauth state")
}
session.DeleteToken(sessionState)
func processGithubUserInfo(code string, c *gin.Context) error {
token, err := oauth.OAuthProvider.GithubConfig.Exchange(oauth2.NoContext, code)
if err != nil {
return fmt.Errorf("invalid google exchange code: %s", err.Error())
@ -165,21 +156,38 @@ func processGithubUserInfo(state string, code string, c *gin.Context) error {
func OAuthCallbackHandler() gin.HandlerFunc {
return func(c *gin.Context) {
log.Println("url:", c.Request.URL)
provider := c.Param("oauth_provider")
state := c.Request.FormValue("state")
log.Println("session state", state)
sessionState := session.GetToken(state)
if sessionState == "" {
c.JSON(400, gin.H{"error": "invalid oauth state"})
}
session.DeleteToken(sessionState)
sessionSplit := strings.Split(state, "___")
log.Println(sessionSplit)
// TODO validate redirect url
if len(sessionSplit) != 2 {
c.JSON(400, gin.H{"error": "invalid redirect url"})
return
}
var err error
code := c.Request.FormValue("code")
switch provider {
case enum.Google.String():
err = processGoogleUserInfo(c.Request.FormValue("state"), c.Request.FormValue("code"), c)
err = processGoogleUserInfo(code, c)
case enum.Github.String():
err = processGithubUserInfo(c.Request.FormValue("state"), c.Request.FormValue("code"), c)
err = processGithubUserInfo(code, c)
default:
err = fmt.Errorf(`invalid oauth provider`)
}
if err != nil {
c.Redirect(http.StatusTemporaryRedirect, constants.FRONTEND_URL+"?error="+err.Error())
c.JSON(400, gin.H{"error": err.Error()})
return
}
c.Redirect(http.StatusTemporaryRedirect, constants.FRONTEND_URL)
c.Redirect(http.StatusTemporaryRedirect, sessionSplit[1])
}
}

View File

@ -10,11 +10,22 @@ import (
"github.com/google/uuid"
)
func OAuthLoginHandler() gin.HandlerFunc {
uuid := uuid.New()
oauthStateString := uuid.String()
// set host in the oauth state that is useful for redirecting
func OAuthLoginHandler() gin.HandlerFunc {
return func(c *gin.Context) {
// TODO validate redirect URL
redirectURL := c.Query("redirectURL")
if redirectURL == "" {
c.JSON(400, gin.H{
"error": "invalid redirect url",
})
return
}
uuid := uuid.New()
oauthStateString := uuid.String() + "___" + redirectURL
provider := c.Param("oauth_provider")
switch provider {

View File

@ -5,7 +5,6 @@ import (
"net/http"
"time"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/session"
@ -63,6 +62,6 @@ func VerifyEmailHandler() gin.HandlerFunc {
session.SetToken(userIdStr, refreshToken)
utils.SetCookie(c, accessToken)
c.Redirect(http.StatusTemporaryRedirect, constants.FRONTEND_URL)
c.Redirect(http.StatusTemporaryRedirect, claim.Host)
}
}

View File

@ -18,6 +18,8 @@ func GinContextToContextMiddleware() gin.HandlerFunc {
}
}
// TODO use allowed origins for cors origin
// TODO throw error if url is not allowed
func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
origin := c.Request.Header.Get("Origin")
@ -44,10 +46,17 @@ func main() {
r := gin.Default()
r.Use(GinContextToContextMiddleware())
r.Use(CORSMiddleware())
r.GET("/", handlers.PlaygroundHandler())
r.POST("/graphql", handlers.GraphqlHandler())
r.GET("/verify_email", handlers.VerifyEmailHandler())
r.GET("/login/:oauth_provider", handlers.OAuthLoginHandler())
r.GET("/callback/:oauth_provider", handlers.OAuthCallbackHandler())
r.GET("/oauth_login/:oauth_provider", handlers.OAuthLoginHandler())
r.GET("/oauth_callback/:oauth_provider", handlers.OAuthCallbackHandler())
// login wall app related routes
r.Static("/app/build", "app/build")
r.LoadHTMLGlob("templates/*")
r.GET("/app", handlers.AppHandler())
r.Run()
}

View File

@ -20,7 +20,7 @@ func InitOAuth() {
OAuthProvider.GoogleConfig = &oauth2.Config{
ClientID: constants.GOOGLE_CLIENT_ID,
ClientSecret: constants.GOOGLE_CLIENT_SECRET,
RedirectURL: constants.AUTHORIZER_DOMAIN + "/callback/google",
RedirectURL: constants.AUTHORIZER_URL + "/oauth_callback/google",
Endpoint: googleOAuth2.Endpoint,
Scopes: []string{"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"},
}
@ -29,7 +29,7 @@ func InitOAuth() {
OAuthProvider.GithubConfig = &oauth2.Config{
ClientID: constants.GITHUB_CLIENT_ID,
ClientSecret: constants.GITHUB_CLIENT_SECRET,
RedirectURL: constants.AUTHORIZER_DOMAIN + "/callback/github",
RedirectURL: constants.AUTHORIZER_URL + "/oauth_callback/github",
Endpoint: githubOAuth2.Endpoint,
}
}
@ -37,7 +37,7 @@ func InitOAuth() {
// OAuthProvider.FacebookConfig = &oauth2.Config{
// ClientID: constants.FACEBOOK_CLIENT_ID,
// ClientSecret: constants.FACEBOOK_CLIENT_SECRET,
// RedirectURL: "/callback/facebook/",
// RedirectURL: "/oauth_callback/facebook/",
// Endpoint: facebookOAuth2.Endpoint,
// }
// }

View File

@ -15,23 +15,27 @@ import (
)
func ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
if err != nil {
return res, err
}
if constants.DISABLE_BASIC_AUTHENTICATION == "true" {
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
}
host := gc.Request.Host
params.Email = strings.ToLower(params.Email)
if !utils.IsValidEmail(params.Email) {
return res, fmt.Errorf("invalid email")
}
_, err := db.Mgr.GetUserByEmail(params.Email)
_, err = db.Mgr.GetUserByEmail(params.Email)
if err != nil {
return res, fmt.Errorf(`user with this email not found`)
}
token, err := utils.CreateVerificationToken(params.Email, enum.ForgotPassword.String())
token, err := utils.CreateVerificationToken(params.Email, enum.ForgotPassword.String(), host)
if err != nil {
log.Println(`Error generating token`, err)
}
@ -44,7 +48,7 @@ func ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*mod
// exec it as go routin so that we can reduce the api latency
go func() {
utils.SendForgotPasswordMail(params.Email, token)
utils.SendForgotPasswordMail(params.Email, token, host)
}()
res = &model.Response{

View File

@ -13,19 +13,25 @@ import (
)
func ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
if err != nil {
return res, err
}
params.Email = strings.ToLower(params.Email)
if !utils.IsValidEmail(params.Email) {
return res, fmt.Errorf("invalid email")
}
host := gc.Request.Host
verificationRequest, err := db.Mgr.GetVerificationByEmail(params.Email)
if err != nil {
return res, fmt.Errorf(`verification request not found`)
}
token, err := utils.CreateVerificationToken(params.Email, verificationRequest.Identifier)
token, err := utils.CreateVerificationToken(params.Email, verificationRequest.Identifier, host)
if err != nil {
log.Println(`Error generating token`, err)
}

View File

@ -84,7 +84,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
if constants.DISABLE_EMAIL_VERICATION != "true" {
// insert verification request
verificationType := enum.BasicAuthSignup.String()
token, err := utils.CreateVerificationToken(params.Email, verificationType)
token, err := utils.CreateVerificationToken(params.Email, verificationType, gc.Request.Host)
if err != nil {
log.Println(`Error generating token`, err)
}

View File

@ -105,7 +105,7 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
hasEmailChanged = true
// insert verification request
verificationType := enum.UpdateEmail.String()
token, err := utils.CreateVerificationToken(newEmail, verificationType)
token, err := utils.CreateVerificationToken(newEmail, verificationType, gc.Request.Host)
if err != nil {
log.Println(`Error generating token`, err)
}
@ -124,6 +124,10 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
}
_, err = db.Mgr.UpdateUser(user)
if err != nil {
log.Println("Error updating user:", err)
return res, err
}
message := `Profile details updated successfully.`
if hasEmailChanged {
message += `For the email change we have sent new verification email, please verify and continue`

View File

@ -12,8 +12,8 @@ func SetCookie(gc *gin.Context, token string) {
secure := true
httpOnly := true
host := GetDomainName(gc.Request.Host)
log.Println("=> host", host)
host := GetHostName(gc.Request.Host)
log.Println("=> cookie host", host)
gc.SetSameSite(http.SameSiteNoneMode)
gc.SetCookie(constants.COOKIE_NAME, token, 3600, "/", host, secure, httpOnly)
}
@ -35,7 +35,7 @@ func DeleteCookie(gc *gin.Context) {
secure = false
}
host := GetDomainName(gc.Request.Host)
host := GetHostName(gc.Request.Host)
gc.SetSameSite(http.SameSiteNoneMode)
gc.SetCookie(constants.COOKIE_NAME, "", -1, "/", host, secure, httpOnly)
}

View File

@ -26,14 +26,14 @@ func SendVerificationMail(toEmail, token string) error {
<a href="%s">Click here to verify</a>
</body>
</html>
`, constants.AUTHORIZER_DOMAIN+"/verify_email"+"?token="+token)
`, constants.AUTHORIZER_URL+"/verify_email"+"?token="+token)
bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
return sender.SendMail(Receiver, Subject, bodyMessage)
}
// SendForgotPasswordMail to send verification email
func SendForgotPasswordMail(toEmail, token string) error {
func SendForgotPasswordMail(toEmail, token, host string) error {
sender := email.NewSender()
// The receiver needs to be in slice as the receive supports multiple receiver
@ -51,7 +51,7 @@ func SendForgotPasswordMail(toEmail, token string) error {
<a href="%s">Reset Password</a>
</body>
</html>
`, constants.FRONTEND_URL+"/"+constants.FORGOT_PASSWORD_URI+"?token="+token)
`, host+"/"+constants.FORGOT_PASSWORD_URI+"?token="+token)
bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
return sender.SendMail(Receiver, Subject, bodyMessage)

View File

@ -2,11 +2,24 @@ package utils
import (
"net/url"
"strings"
)
// function to get hostname
func GetHostName(auth_url string) string {
u, err := url.Parse(auth_url)
if err != nil {
return `localhost`
}
host := u.Hostname()
return host
}
// function to get domain name
func GetDomainName(auth_url string) string {
u, err := url.Parse("//" + auth_url)
u, err := url.Parse(auth_url)
if err != nil {
return `localhost`
}
@ -14,24 +27,24 @@ func GetDomainName(auth_url string) string {
host := u.Hostname()
// code to get root domain in case of sub-domains
// hostParts := strings.Split(host, ".")
// hostPartsLen := len(hostParts)
hostParts := strings.Split(host, ".")
hostPartsLen := len(hostParts)
// if hostPartsLen == 1 {
// return host
// }
if hostPartsLen == 1 {
return host
}
// if hostPartsLen == 2 {
// if hostParts[0] == "www" {
// return hostParts[1]
// } else {
// return host
// }
// }
if hostPartsLen == 2 {
if hostParts[0] == "www" {
return hostParts[1]
} else {
return host
}
}
// if hostPartsLen > 2 {
// return strings.Join(hostParts[hostPartsLen-2:], ".")
// }
if hostPartsLen > 2 {
return strings.Join(hostParts[hostPartsLen-2:], ".")
}
return host
}

View File

@ -1,8 +1,31 @@
package utils
import "net/mail"
import (
"net/mail"
"strings"
"github.com/authorizerdev/authorizer/server/constants"
)
func IsValidEmail(email string) bool {
_, err := mail.ParseAddress(email)
return err == nil
}
func IsValidRedirectURL(url string) bool {
if len(constants.ALLOWED_ORIGINS) == 1 && constants.ALLOWED_ORIGINS[0] == "*" {
return true
}
hasValidURL := false
urlDomain := GetDomainName(url)
for _, val := range constants.ALLOWED_ORIGINS {
if strings.Contains(val, urlDomain) {
hasValidURL = true
break
}
}
return hasValidURL
}

View File

@ -9,6 +9,7 @@ import (
type UserInfo struct {
Email string `json:"email"`
Host string `json:"host"`
}
type CustomClaim struct {
@ -18,7 +19,7 @@ type CustomClaim struct {
}
// TODO convert tokenType to enum
func CreateVerificationToken(email string, tokenType string) (string, error) {
func CreateVerificationToken(email string, tokenType string, host string) (string, error) {
t := jwt.New(jwt.GetSigningMethod(constants.JWT_TYPE))
t.Claims = &CustomClaim{
@ -27,7 +28,7 @@ func CreateVerificationToken(email string, tokenType string) (string, error) {
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
},
tokenType,
UserInfo{Email: email},
UserInfo{Email: email, Host: host},
}
return t.SignedString([]byte(constants.JWT_SECRET))

17
templates/app.tmpl Normal file
View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="/app/build/bundle.css">
<script>
window.__authorizer__ = {{.data}}
</script>
</head>
<body>
<div id="root"></div>
<script src="/app/build/bundle.js"></script>
</body>
</html>