Compare commits

...

11 Commits

Author SHA1 Message Date
Lakhan Samani
7a2dbea019 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-06-09 23:43:28 +05:30
Lakhan Samani
dff50097e8 feat: add support for cockroachdb 2022-06-09 23:43:21 +05:30
Lakhan Samani
aff9d3af20 Merge pull request #187 from authorizerdev/fix-parallel-access
fix: parallel access of env vars
2022-06-09 23:13:34 +05:30
Lakhan Samani
02eb1d6677 fix: add const for test env 2022-06-09 23:13:22 +05:30
Lakhan Samani
78a673e4ad fix: fix parallel access of env vars 2022-06-08 09:50:30 +05:30
Lakhan Samani
e0d8644264 fix: role validation while signup 2022-06-07 08:00:30 +05:30
Lakhan Samani
d8c662eaad fix: dashboard roles 2022-06-07 07:30:01 +05:30
Lakhan Samani
6d1d259f71 Merge pull request #182 from authorizerdev/feat/add-linkedin-login
feat: add linkedin login
2022-06-06 22:09:08 +05:30
Lakhan Samani
2841853d37 feat: add linkedin login 2022-06-06 22:08:32 +05:30
Lakhan Samani
360dd3c3bd fix: redirect uri 2022-06-05 22:46:56 +05:30
Lakhan Samani
c6add0cca6 fix: give higher priority to authorizer url 2022-06-05 22:13:10 +05:30
29 changed files with 500 additions and 123 deletions

30
app/package-lock.json generated
View File

@@ -9,7 +9,7 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-react": "^0.17.0", "@authorizerdev/authorizer-react": "^0.23.0",
"@types/react": "^17.0.15", "@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9", "@types/react-dom": "^17.0.9",
"esbuild": "^0.12.17", "esbuild": "^0.12.17",
@@ -26,9 +26,9 @@
} }
}, },
"node_modules/@authorizerdev/authorizer-js": { "node_modules/@authorizerdev/authorizer-js": {
"version": "0.10.0", "version": "0.12.0",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.10.0.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.12.0.tgz",
"integrity": "sha512-REM8FLD/Ej9gzA2zDGDAke6QFss33ubePlTDmLDmIYUuQmpHFlO5mCCS6nVsKkN7F/Bcwkmp+eUNQjkdGCaKLg==", "integrity": "sha512-XgRxAkpRobbp15DeHygfOebCxlPJAXbVaLDckYyuz/PUDTyeMIG65RV5rQHYcL4oeoPqNc42dewwM3ST8JSiNg==",
"dependencies": { "dependencies": {
"node-fetch": "^2.6.1" "node-fetch": "^2.6.1"
}, },
@@ -37,11 +37,11 @@
} }
}, },
"node_modules/@authorizerdev/authorizer-react": { "node_modules/@authorizerdev/authorizer-react": {
"version": "0.17.0", "version": "0.23.0",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.17.0.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.23.0.tgz",
"integrity": "sha512-7WcNCU7hDFkVfFb8LcJXFwWiLYd8aY78z1AbNPxCa2Cw5G85PaRkzjKybP6h01ITVOHO6M03lLwPj8p6Sr6fEg==", "integrity": "sha512-vOwwrrAorxhVsqpf3BO2In8PMg8RAbGBFu8uLDOvUzkwG0ny5CPg6jLx9+dCkRRsqgB+agBoQoIuXEUP0ijsTA==",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-js": "^0.10.0", "@authorizerdev/authorizer-js": "^0.12.0",
"final-form": "^4.20.2", "final-form": "^4.20.2",
"react-final-form": "^6.5.3", "react-final-form": "^6.5.3",
"styled-components": "^5.3.0" "styled-components": "^5.3.0"
@@ -852,19 +852,19 @@
}, },
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-js": { "@authorizerdev/authorizer-js": {
"version": "0.10.0", "version": "0.12.0",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.10.0.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.12.0.tgz",
"integrity": "sha512-REM8FLD/Ej9gzA2zDGDAke6QFss33ubePlTDmLDmIYUuQmpHFlO5mCCS6nVsKkN7F/Bcwkmp+eUNQjkdGCaKLg==", "integrity": "sha512-XgRxAkpRobbp15DeHygfOebCxlPJAXbVaLDckYyuz/PUDTyeMIG65RV5rQHYcL4oeoPqNc42dewwM3ST8JSiNg==",
"requires": { "requires": {
"node-fetch": "^2.6.1" "node-fetch": "^2.6.1"
} }
}, },
"@authorizerdev/authorizer-react": { "@authorizerdev/authorizer-react": {
"version": "0.17.0", "version": "0.23.0",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.17.0.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.23.0.tgz",
"integrity": "sha512-7WcNCU7hDFkVfFb8LcJXFwWiLYd8aY78z1AbNPxCa2Cw5G85PaRkzjKybP6h01ITVOHO6M03lLwPj8p6Sr6fEg==", "integrity": "sha512-vOwwrrAorxhVsqpf3BO2In8PMg8RAbGBFu8uLDOvUzkwG0ny5CPg6jLx9+dCkRRsqgB+agBoQoIuXEUP0ijsTA==",
"requires": { "requires": {
"@authorizerdev/authorizer-js": "^0.10.0", "@authorizerdev/authorizer-js": "^0.12.0",
"final-form": "^4.20.2", "final-form": "^4.20.2",
"react-final-form": "^6.5.3", "react-final-form": "^6.5.3",
"styled-components": "^5.3.0" "styled-components": "^5.3.0"

View File

@@ -11,7 +11,7 @@
"author": "Lakhan Samani", "author": "Lakhan Samani",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-react": "^0.17.0", "@authorizerdev/authorizer-react": "^0.23.0",
"@types/react": "^17.0.15", "@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9", "@types/react-dom": "^17.0.9",
"esbuild": "^0.12.17", "esbuild": "^0.12.17",

View File

@@ -9,7 +9,7 @@ import {
Divider, Divider,
useMediaQuery, useMediaQuery,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { FaGoogle, FaGithub, FaFacebookF } from 'react-icons/fa'; import { FaGoogle, FaGithub, FaFacebookF, FaLinkedin } from 'react-icons/fa';
import { TextInputType, HiddenInputType } from '../../constants'; import { TextInputType, HiddenInputType } from '../../constants';
const OAuthConfig = ({ const OAuthConfig = ({
@@ -182,6 +182,44 @@ const OAuthConfig = ({
/> />
</Center> </Center>
</Flex> </Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaLinkedin style={{ color: '#3b5998' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.LINKEDIN_CLIENT_ID}
placeholder="LinkedIn Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.LINKEDIN_CLIENT_SECRET}
placeholder="LinkedIn Secret"
/>
</Center>
</Flex>
</Stack> </Stack>
</Box> </Box>
</div> </div>

View File

@@ -1,67 +1,68 @@
import React from "react"; import React from 'react';
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react"; import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
import { ArrayInputType } from "../../constants"; import { ArrayInputType } from '../../constants';
import InputField from "../InputField"; import InputField from '../InputField';
const Roles = ({ variables, setVariables }: any) => { const Roles = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)"); const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div> return (
{" "} <div>
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}> {' '}
Roles <Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
</Text> Roles
<Stack spacing={6} padding="2% 0%"> </Text>
<Flex direction={isNotSmallerScreen ? "row" : "column"}> <Stack spacing={6} padding="2% 0%">
<Flex w="30%" justifyContent="start" alignItems="center"> <Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Text fontSize="sm">Roles:</Text> <Flex w="30%" justifyContent="start" alignItems="center">
</Flex> <Text fontSize="sm">Roles:</Text>
<Center </Flex>
w={isNotSmallerScreen ? "70%" : "100%"} <Center
mt={isNotSmallerScreen ? "0" : "2"} w={isNotSmallerScreen ? '70%' : '100%'}
overflow="hidden" mt={isNotSmallerScreen ? '0' : '2'}
> overflow="hidden"
<InputField >
borderRadius={7} <InputField
variables={variables} borderRadius={7}
setVariables={setVariables} variables={variables}
inputType={ArrayInputType.ROLES} setVariables={setVariables}
/> inputType={ArrayInputType.ROLES}
</Center> />
</Flex> </Center>
<Flex direction={isNotSmallerScreen ? "row" : "column"}> </Flex>
<Flex w="30%" justifyContent="start" alignItems="center"> <Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Text fontSize="sm">Default Roles:</Text> <Flex w="30%" justifyContent="start" alignItems="center">
</Flex> <Text fontSize="sm">Default Roles:</Text>
<Center </Flex>
w={isNotSmallerScreen ? "70%" : "100%"} <Center
mt={isNotSmallerScreen ? "0" : "2"} w={isNotSmallerScreen ? '70%' : '100%'}
> mt={isNotSmallerScreen ? '0' : '2'}
<InputField >
variables={variables} <InputField
setVariables={setVariables} variables={variables}
inputType={ArrayInputType.DEFAULT_ROLES} setVariables={setVariables}
/> inputType={ArrayInputType.DEFAULT_ROLES}
</Center> />
</Flex> </Center>
<Flex direction={isNotSmallerScreen ? "row" : "column"}> </Flex>
<Flex w="30%" justifyContent="start" alignItems="center"> <Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Text fontSize="sm">Protected Roles:</Text> <Flex w="30%" justifyContent="start" alignItems="center">
</Flex> <Text fontSize="sm">Protected Roles:</Text>
<Center </Flex>
w={isNotSmallerScreen ? "70%" : "100%"} <Center
mt={isNotSmallerScreen ? "0" : "2"} w={isNotSmallerScreen ? '70%' : '100%'}
> mt={isNotSmallerScreen ? '0' : '2'}
<InputField >
variables={variables} <InputField
setVariables={setVariables} variables={variables}
inputType={ArrayInputType.PROTECTED_ROLES} setVariables={setVariables}
/> inputType={ArrayInputType.PROTECTED_ROLES}
</Center> />
</Flex> </Center>
</Stack> </Flex>
</div> </Stack>
); </div>
);
}; };
export default Roles; export default Roles;

View File

@@ -7,6 +7,7 @@ export const TextInputType = {
GOOGLE_CLIENT_ID: 'GOOGLE_CLIENT_ID', GOOGLE_CLIENT_ID: 'GOOGLE_CLIENT_ID',
GITHUB_CLIENT_ID: 'GITHUB_CLIENT_ID', GITHUB_CLIENT_ID: 'GITHUB_CLIENT_ID',
FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID', FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID',
LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID',
JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM', JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM',
REDIS_URL: 'REDIS_URL', REDIS_URL: 'REDIS_URL',
SMTP_HOST: 'SMTP_HOST', SMTP_HOST: 'SMTP_HOST',
@@ -31,6 +32,7 @@ export const HiddenInputType = {
GOOGLE_CLIENT_SECRET: 'GOOGLE_CLIENT_SECRET', GOOGLE_CLIENT_SECRET: 'GOOGLE_CLIENT_SECRET',
GITHUB_CLIENT_SECRET: 'GITHUB_CLIENT_SECRET', GITHUB_CLIENT_SECRET: 'GITHUB_CLIENT_SECRET',
FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET', FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET',
LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET',
JWT_SECRET: 'JWT_SECRET', JWT_SECRET: 'JWT_SECRET',
SMTP_PASSWORD: 'SMTP_PASSWORD', SMTP_PASSWORD: 'SMTP_PASSWORD',
ADMIN_SECRET: 'ADMIN_SECRET', ADMIN_SECRET: 'ADMIN_SECRET',
@@ -99,6 +101,8 @@ export interface envVarTypes {
GITHUB_CLIENT_SECRET: string; GITHUB_CLIENT_SECRET: string;
FACEBOOK_CLIENT_ID: string; FACEBOOK_CLIENT_ID: string;
FACEBOOK_CLIENT_SECRET: string; FACEBOOK_CLIENT_SECRET: string;
LINKEDIN_CLIENT_ID: string;
LINKEDIN_CLIENT_SECRET: string;
ROLES: [string] | []; ROLES: [string] | [];
DEFAULT_ROLES: [string] | []; DEFAULT_ROLES: [string] | [];
PROTECTED_ROLES: [string] | []; PROTECTED_ROLES: [string] | [];

View File

@@ -26,9 +26,11 @@ export const EnvVariablesQuery = `
GITHUB_CLIENT_SECRET, GITHUB_CLIENT_SECRET,
FACEBOOK_CLIENT_ID, FACEBOOK_CLIENT_ID,
FACEBOOK_CLIENT_SECRET, FACEBOOK_CLIENT_SECRET,
ROLES, LINKEDIN_CLIENT_ID,
LINKEDIN_CLIENT_SECRET,
DEFAULT_ROLES, DEFAULT_ROLES,
PROTECTED_ROLES, PROTECTED_ROLES,
ROLES,
JWT_TYPE, JWT_TYPE,
JWT_SECRET, JWT_SECRET,
JWT_ROLE_CLAIM, JWT_ROLE_CLAIM,

View File

@@ -46,6 +46,8 @@ const Environment = () => {
GITHUB_CLIENT_SECRET: '', GITHUB_CLIENT_SECRET: '',
FACEBOOK_CLIENT_ID: '', FACEBOOK_CLIENT_ID: '',
FACEBOOK_CLIENT_SECRET: '', FACEBOOK_CLIENT_SECRET: '',
LINKEDIN_CLIENT_ID: '',
LINKEDIN_CLIENT_SECRET: '',
ROLES: [], ROLES: [],
DEFAULT_ROLES: [], DEFAULT_ROLES: [],
PROTECTED_ROLES: [], PROTECTED_ROLES: [],
@@ -83,6 +85,7 @@ const Environment = () => {
GOOGLE_CLIENT_SECRET: false, GOOGLE_CLIENT_SECRET: false,
GITHUB_CLIENT_SECRET: false, GITHUB_CLIENT_SECRET: false,
FACEBOOK_CLIENT_SECRET: false, FACEBOOK_CLIENT_SECRET: false,
LINKEDIN_CLIENT_SECRET: false,
JWT_SECRET: false, JWT_SECRET: false,
SMTP_PASSWORD: false, SMTP_PASSWORD: false,
ADMIN_SECRET: false, ADMIN_SECRET: false,

View File

@@ -21,4 +21,6 @@ const (
DbTypeCassandraDB = "cassandradb" DbTypeCassandraDB = "cassandradb"
// DbTypeScyllaDB is the scylla database type // DbTypeScyllaDB is the scylla database type
DbTypeScyllaDB = "scylladb" DbTypeScyllaDB = "scylladb"
// DbTypeCockroachDB is the cockroach database type
DbTypeCockroachDB = "cockroachdb"
) )

View File

@@ -3,6 +3,8 @@ package constants
var VERSION = "0.0.1" var VERSION = "0.0.1"
const ( const (
// TestEnv is used for testing
TestEnv = "test"
// EnvKeyEnv key for env variable ENV // EnvKeyEnv key for env variable ENV
EnvKeyEnv = "ENV" EnvKeyEnv = "ENV"
// EnvKeyEnvPath key for cli arg variable ENV_PATH // EnvKeyEnvPath key for cli arg variable ENV_PATH
@@ -73,6 +75,10 @@ const (
EnvKeyFacebookClientID = "FACEBOOK_CLIENT_ID" EnvKeyFacebookClientID = "FACEBOOK_CLIENT_ID"
// EnvKeyFacebookClientSecret key for env variable FACEBOOK_CLIENT_SECRET // EnvKeyFacebookClientSecret key for env variable FACEBOOK_CLIENT_SECRET
EnvKeyFacebookClientSecret = "FACEBOOK_CLIENT_SECRET" EnvKeyFacebookClientSecret = "FACEBOOK_CLIENT_SECRET"
// EnvKeyLinkedinClientID key for env variable LINKEDIN_CLIENT_ID
EnvKeyLinkedInClientID = "LINKEDIN_CLIENT_ID"
// EnvKeyLinkedinClientSecret key for env variable LINKEDIN_CLIENT_SECRET
EnvKeyLinkedInClientSecret = "LINKEDIN_CLIENT_SECRET"
// EnvKeyOrganizationName key for env variable ORGANIZATION_NAME // EnvKeyOrganizationName key for env variable ORGANIZATION_NAME
EnvKeyOrganizationName = "ORGANIZATION_NAME" EnvKeyOrganizationName = "ORGANIZATION_NAME"
// EnvKeyOrganizationLogo key for env variable ORGANIZATION_LOGO // EnvKeyOrganizationLogo key for env variable ORGANIZATION_LOGO

View File

@@ -8,4 +8,7 @@ const (
FacebookUserInfoURL = "https://graph.facebook.com/me?fields=id,first_name,last_name,name,email,picture&access_token=" FacebookUserInfoURL = "https://graph.facebook.com/me?fields=id,first_name,last_name,name,email,picture&access_token="
// Ref: https://docs.github.com/en/developers/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps#3-your-github-app-accesses-the-api-with-the-users-access-token // Ref: https://docs.github.com/en/developers/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps#3-your-github-app-accesses-the-api-with-the-users-access-token
GithubUserInfoURL = "https://api.github.com/user" GithubUserInfoURL = "https://api.github.com/user"
// Ref: https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api
LinkedInUserInfoURL = "https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName,emailAddress,profilePicture(displayImage~:playableStreams))"
LinkedInEmailURL = "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))"
) )

View File

@@ -11,4 +11,6 @@ const (
SignupMethodGithub = "github" SignupMethodGithub = "github"
// SignupMethodFacebook is the facebook signup method // SignupMethodFacebook is the facebook signup method
SignupMethodFacebook = "facebook" SignupMethodFacebook = "facebook"
// SignupMethodLinkedin is the linkedin signup method
SignupMethodLinkedIn = "linkedin"
) )

View File

@@ -46,7 +46,7 @@ func NewProvider() (*provider, error) {
dbURL := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseURL dbURL := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseURL
switch dbType { switch dbType {
case constants.DbTypePostgres, constants.DbTypeYugabyte: case constants.DbTypePostgres, constants.DbTypeYugabyte, constants.DbTypeCockroachDB:
sqlDB, err = gorm.Open(postgres.Open(dbURL), ormConfig) sqlDB, err = gorm.Open(postgres.Open(dbURL), ormConfig)
case constants.DbTypeSqlite: case constants.DbTypeSqlite:
sqlDB, err = gorm.Open(sqlite.Open(dbURL), ormConfig) sqlDB, err = gorm.Open(sqlite.Open(dbURL), ormConfig)

View File

@@ -37,7 +37,7 @@ func SendMail(to []string, Subject, bodyMessage string) error {
if err != nil { if err != nil {
return err return err
} }
if envKey == "test" { if envKey == constants.TestEnv {
return nil return nil
} }
m := gomail.NewMessage() m := gomail.NewMessage()

16
server/env/env.go vendored
View File

@@ -68,6 +68,8 @@ func InitAllEnv() error {
osGithubClientSecret := os.Getenv(constants.EnvKeyGithubClientSecret) osGithubClientSecret := os.Getenv(constants.EnvKeyGithubClientSecret)
osFacebookClientID := os.Getenv(constants.EnvKeyFacebookClientID) osFacebookClientID := os.Getenv(constants.EnvKeyFacebookClientID)
osFacebookClientSecret := os.Getenv(constants.EnvKeyFacebookClientSecret) osFacebookClientSecret := os.Getenv(constants.EnvKeyFacebookClientSecret)
osLinkedInClientID := os.Getenv(constants.EnvKeyLinkedInClientID)
osLinkedInClientSecret := os.Getenv(constants.EnvKeyLinkedInClientSecret)
osResetPasswordURL := os.Getenv(constants.EnvKeyResetPasswordURL) osResetPasswordURL := os.Getenv(constants.EnvKeyResetPasswordURL)
osOrganizationName := os.Getenv(constants.EnvKeyOrganizationName) osOrganizationName := os.Getenv(constants.EnvKeyOrganizationName)
osOrganizationLogo := os.Getenv(constants.EnvKeyOrganizationLogo) osOrganizationLogo := os.Getenv(constants.EnvKeyOrganizationLogo)
@@ -345,6 +347,20 @@ func InitAllEnv() error {
envData[constants.EnvKeyFacebookClientSecret] = osFacebookClientSecret envData[constants.EnvKeyFacebookClientSecret] = osFacebookClientSecret
} }
if val, ok := envData[constants.EnvKeyLinkedInClientID]; !ok || val == "" {
envData[constants.EnvKeyLinkedInClientID] = osLinkedInClientID
}
if osFacebookClientID != "" && envData[constants.EnvKeyLinkedInClientID] != osFacebookClientID {
envData[constants.EnvKeyLinkedInClientID] = osLinkedInClientID
}
if val, ok := envData[constants.EnvKeyLinkedInClientSecret]; !ok || val == "" {
envData[constants.EnvKeyLinkedInClientSecret] = osLinkedInClientSecret
}
if osFacebookClientSecret != "" && envData[constants.EnvKeyLinkedInClientSecret] != osFacebookClientSecret {
envData[constants.EnvKeyLinkedInClientSecret] = osLinkedInClientSecret
}
if val, ok := envData[constants.EnvKeyResetPasswordURL]; !ok || val == "" { if val, ok := envData[constants.EnvKeyResetPasswordURL]; !ok || val == "" {
envData[constants.EnvKeyResetPasswordURL] = strings.TrimPrefix(osResetPasswordURL, "/") envData[constants.EnvKeyResetPasswordURL] = strings.TrimPrefix(osResetPasswordURL, "/")
} }

View File

@@ -85,6 +85,8 @@ type ComplexityRoot struct {
JwtRoleClaim func(childComplexity int) int JwtRoleClaim func(childComplexity int) int
JwtSecret func(childComplexity int) int JwtSecret func(childComplexity int) int
JwtType func(childComplexity int) int JwtType func(childComplexity int) int
LinkedinClientID func(childComplexity int) int
LinkedinClientSecret func(childComplexity int) int
OrganizationLogo func(childComplexity int) int OrganizationLogo func(childComplexity int) int
OrganizationName func(childComplexity int) int OrganizationName func(childComplexity int) int
ProtectedRoles func(childComplexity int) int ProtectedRoles func(childComplexity int) int
@@ -116,6 +118,7 @@ type ComplexityRoot struct {
IsFacebookLoginEnabled func(childComplexity int) int IsFacebookLoginEnabled func(childComplexity int) int
IsGithubLoginEnabled func(childComplexity int) int IsGithubLoginEnabled func(childComplexity int) int
IsGoogleLoginEnabled func(childComplexity int) int IsGoogleLoginEnabled func(childComplexity int) int
IsLinkedinLoginEnabled func(childComplexity int) int
IsMagicLinkLoginEnabled func(childComplexity int) int IsMagicLinkLoginEnabled func(childComplexity int) int
IsSignUpEnabled func(childComplexity int) int IsSignUpEnabled func(childComplexity int) int
Version func(childComplexity int) int Version func(childComplexity int) int
@@ -528,6 +531,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Env.JwtType(childComplexity), true return e.complexity.Env.JwtType(childComplexity), true
case "Env.LINKEDIN_CLIENT_ID":
if e.complexity.Env.LinkedinClientID == nil {
break
}
return e.complexity.Env.LinkedinClientID(childComplexity), true
case "Env.LINKEDIN_CLIENT_SECRET":
if e.complexity.Env.LinkedinClientSecret == nil {
break
}
return e.complexity.Env.LinkedinClientSecret(childComplexity), true
case "Env.ORGANIZATION_LOGO": case "Env.ORGANIZATION_LOGO":
if e.complexity.Env.OrganizationLogo == nil { if e.complexity.Env.OrganizationLogo == nil {
break break
@@ -682,6 +699,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Meta.IsGoogleLoginEnabled(childComplexity), true return e.complexity.Meta.IsGoogleLoginEnabled(childComplexity), true
case "Meta.is_linkedin_login_enabled":
if e.complexity.Meta.IsLinkedinLoginEnabled == nil {
break
}
return e.complexity.Meta.IsLinkedinLoginEnabled(childComplexity), true
case "Meta.is_magic_link_login_enabled": case "Meta.is_magic_link_login_enabled":
if e.complexity.Meta.IsMagicLinkLoginEnabled == nil { if e.complexity.Meta.IsMagicLinkLoginEnabled == nil {
break break
@@ -1352,6 +1376,7 @@ type Meta {
is_google_login_enabled: Boolean! is_google_login_enabled: Boolean!
is_facebook_login_enabled: Boolean! is_facebook_login_enabled: Boolean!
is_github_login_enabled: Boolean! is_github_login_enabled: Boolean!
is_linkedin_login_enabled: Boolean!
is_email_verification_enabled: Boolean! is_email_verification_enabled: Boolean!
is_basic_authentication_enabled: Boolean! is_basic_authentication_enabled: Boolean!
is_magic_link_login_enabled: Boolean! is_magic_link_login_enabled: Boolean!
@@ -1462,6 +1487,8 @@ type Env {
GITHUB_CLIENT_SECRET: String GITHUB_CLIENT_SECRET: String
FACEBOOK_CLIENT_ID: String FACEBOOK_CLIENT_ID: String
FACEBOOK_CLIENT_SECRET: String FACEBOOK_CLIENT_SECRET: String
LINKEDIN_CLIENT_ID: String
LINKEDIN_CLIENT_SECRET: String
ORGANIZATION_NAME: String ORGANIZATION_NAME: String
ORGANIZATION_LOGO: String ORGANIZATION_LOGO: String
} }
@@ -1509,6 +1536,8 @@ input UpdateEnvInput {
GITHUB_CLIENT_SECRET: String GITHUB_CLIENT_SECRET: String
FACEBOOK_CLIENT_ID: String FACEBOOK_CLIENT_ID: String
FACEBOOK_CLIENT_SECRET: String FACEBOOK_CLIENT_SECRET: String
LINKEDIN_CLIENT_ID: String
LINKEDIN_CLIENT_SECRET: String
ORGANIZATION_NAME: String ORGANIZATION_NAME: String
ORGANIZATION_LOGO: String ORGANIZATION_LOGO: String
} }
@@ -3602,6 +3631,70 @@ func (ec *executionContext) _Env_FACEBOOK_CLIENT_SECRET(ctx context.Context, fie
return ec.marshalOString2ᚖstring(ctx, field.Selections, res) return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
} }
func (ec *executionContext) _Env_LINKEDIN_CLIENT_ID(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Env",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.LinkedinClientID, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*string)
fc.Result = res
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}
func (ec *executionContext) _Env_LINKEDIN_CLIENT_SECRET(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Env",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.LinkedinClientSecret, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*string)
fc.Result = res
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}
func (ec *executionContext) _Env_ORGANIZATION_NAME(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) { func (ec *executionContext) _Env_ORGANIZATION_NAME(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@@ -4007,6 +4100,41 @@ func (ec *executionContext) _Meta_is_github_login_enabled(ctx context.Context, f
return ec.marshalNBoolean2bool(ctx, field.Selections, res) return ec.marshalNBoolean2bool(ctx, field.Selections, res)
} }
func (ec *executionContext) _Meta_is_linkedin_login_enabled(ctx context.Context, field graphql.CollectedField, obj *model.Meta) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Meta",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.IsLinkedinLoginEnabled, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(bool)
fc.Result = res
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
func (ec *executionContext) _Meta_is_email_verification_enabled(ctx context.Context, field graphql.CollectedField, obj *model.Meta) (ret graphql.Marshaler) { func (ec *executionContext) _Meta_is_email_verification_enabled(ctx context.Context, field graphql.CollectedField, obj *model.Meta) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@@ -8563,6 +8691,22 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob
if err != nil { if err != nil {
return it, err return it, err
} }
case "LINKEDIN_CLIENT_ID":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("LINKEDIN_CLIENT_ID"))
it.LinkedinClientID, err = ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
case "LINKEDIN_CLIENT_SECRET":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("LINKEDIN_CLIENT_SECRET"))
it.LinkedinClientSecret, err = ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
case "ORGANIZATION_NAME": case "ORGANIZATION_NAME":
var err error var err error
@@ -9031,6 +9175,10 @@ func (ec *executionContext) _Env(ctx context.Context, sel ast.SelectionSet, obj
out.Values[i] = ec._Env_FACEBOOK_CLIENT_ID(ctx, field, obj) out.Values[i] = ec._Env_FACEBOOK_CLIENT_ID(ctx, field, obj)
case "FACEBOOK_CLIENT_SECRET": case "FACEBOOK_CLIENT_SECRET":
out.Values[i] = ec._Env_FACEBOOK_CLIENT_SECRET(ctx, field, obj) out.Values[i] = ec._Env_FACEBOOK_CLIENT_SECRET(ctx, field, obj)
case "LINKEDIN_CLIENT_ID":
out.Values[i] = ec._Env_LINKEDIN_CLIENT_ID(ctx, field, obj)
case "LINKEDIN_CLIENT_SECRET":
out.Values[i] = ec._Env_LINKEDIN_CLIENT_SECRET(ctx, field, obj)
case "ORGANIZATION_NAME": case "ORGANIZATION_NAME":
out.Values[i] = ec._Env_ORGANIZATION_NAME(ctx, field, obj) out.Values[i] = ec._Env_ORGANIZATION_NAME(ctx, field, obj)
case "ORGANIZATION_LOGO": case "ORGANIZATION_LOGO":
@@ -9142,6 +9290,11 @@ func (ec *executionContext) _Meta(ctx context.Context, sel ast.SelectionSet, obj
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "is_linkedin_login_enabled":
out.Values[i] = ec._Meta_is_linkedin_login_enabled(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "is_email_verification_enabled": case "is_email_verification_enabled":
out.Values[i] = ec._Meta_is_email_verification_enabled(ctx, field, obj) out.Values[i] = ec._Meta_is_email_verification_enabled(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {

View File

@@ -65,6 +65,8 @@ type Env struct {
GithubClientSecret *string `json:"GITHUB_CLIENT_SECRET"` GithubClientSecret *string `json:"GITHUB_CLIENT_SECRET"`
FacebookClientID *string `json:"FACEBOOK_CLIENT_ID"` FacebookClientID *string `json:"FACEBOOK_CLIENT_ID"`
FacebookClientSecret *string `json:"FACEBOOK_CLIENT_SECRET"` FacebookClientSecret *string `json:"FACEBOOK_CLIENT_SECRET"`
LinkedinClientID *string `json:"LINKEDIN_CLIENT_ID"`
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET"`
OrganizationName *string `json:"ORGANIZATION_NAME"` OrganizationName *string `json:"ORGANIZATION_NAME"`
OrganizationLogo *string `json:"ORGANIZATION_LOGO"` OrganizationLogo *string `json:"ORGANIZATION_LOGO"`
} }
@@ -116,6 +118,7 @@ type Meta struct {
IsGoogleLoginEnabled bool `json:"is_google_login_enabled"` IsGoogleLoginEnabled bool `json:"is_google_login_enabled"`
IsFacebookLoginEnabled bool `json:"is_facebook_login_enabled"` IsFacebookLoginEnabled bool `json:"is_facebook_login_enabled"`
IsGithubLoginEnabled bool `json:"is_github_login_enabled"` IsGithubLoginEnabled bool `json:"is_github_login_enabled"`
IsLinkedinLoginEnabled bool `json:"is_linkedin_login_enabled"`
IsEmailVerificationEnabled bool `json:"is_email_verification_enabled"` IsEmailVerificationEnabled bool `json:"is_email_verification_enabled"`
IsBasicAuthenticationEnabled bool `json:"is_basic_authentication_enabled"` IsBasicAuthenticationEnabled bool `json:"is_basic_authentication_enabled"`
IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"` IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"`
@@ -216,6 +219,8 @@ type UpdateEnvInput struct {
GithubClientSecret *string `json:"GITHUB_CLIENT_SECRET"` GithubClientSecret *string `json:"GITHUB_CLIENT_SECRET"`
FacebookClientID *string `json:"FACEBOOK_CLIENT_ID"` FacebookClientID *string `json:"FACEBOOK_CLIENT_ID"`
FacebookClientSecret *string `json:"FACEBOOK_CLIENT_SECRET"` FacebookClientSecret *string `json:"FACEBOOK_CLIENT_SECRET"`
LinkedinClientID *string `json:"LINKEDIN_CLIENT_ID"`
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET"`
OrganizationName *string `json:"ORGANIZATION_NAME"` OrganizationName *string `json:"ORGANIZATION_NAME"`
OrganizationLogo *string `json:"ORGANIZATION_LOGO"` OrganizationLogo *string `json:"ORGANIZATION_LOGO"`
} }

View File

@@ -18,6 +18,7 @@ type Meta {
is_google_login_enabled: Boolean! is_google_login_enabled: Boolean!
is_facebook_login_enabled: Boolean! is_facebook_login_enabled: Boolean!
is_github_login_enabled: Boolean! is_github_login_enabled: Boolean!
is_linkedin_login_enabled: Boolean!
is_email_verification_enabled: Boolean! is_email_verification_enabled: Boolean!
is_basic_authentication_enabled: Boolean! is_basic_authentication_enabled: Boolean!
is_magic_link_login_enabled: Boolean! is_magic_link_login_enabled: Boolean!
@@ -128,6 +129,8 @@ type Env {
GITHUB_CLIENT_SECRET: String GITHUB_CLIENT_SECRET: String
FACEBOOK_CLIENT_ID: String FACEBOOK_CLIENT_ID: String
FACEBOOK_CLIENT_SECRET: String FACEBOOK_CLIENT_SECRET: String
LINKEDIN_CLIENT_ID: String
LINKEDIN_CLIENT_SECRET: String
ORGANIZATION_NAME: String ORGANIZATION_NAME: String
ORGANIZATION_LOGO: String ORGANIZATION_LOGO: String
} }
@@ -175,6 +178,8 @@ input UpdateEnvInput {
GITHUB_CLIENT_SECRET: String GITHUB_CLIENT_SECRET: String
FACEBOOK_CLIENT_ID: String FACEBOOK_CLIENT_ID: String
FACEBOOK_CLIENT_SECRET: String FACEBOOK_CLIENT_SECRET: String
LINKEDIN_CLIENT_ID: String
LINKEDIN_CLIENT_SECRET: String
ORGANIZATION_NAME: String ORGANIZATION_NAME: String
ORGANIZATION_LOGO: String ORGANIZATION_LOGO: String
} }

View File

@@ -60,6 +60,8 @@ func OAuthCallbackHandler() gin.HandlerFunc {
user, err = processGithubUserInfo(code) user, err = processGithubUserInfo(code)
case constants.SignupMethodFacebook: case constants.SignupMethodFacebook:
user, err = processFacebookUserInfo(code) user, err = processFacebookUserInfo(code)
case constants.SignupMethodLinkedIn:
user, err = processLinkedInUserInfo(code)
default: default:
log.Info("Invalid oauth provider") log.Info("Invalid oauth provider")
err = fmt.Errorf(`invalid oauth provider`) err = fmt.Errorf(`invalid oauth provider`)
@@ -214,7 +216,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
if strings.Contains(redirectURL, "?") { if strings.Contains(redirectURL, "?") {
redirectURL = redirectURL + "&" + params redirectURL = redirectURL + "&" + params
} else { } else {
redirectURL = redirectURL + "?" + params redirectURL = redirectURL + "?" + strings.TrimPrefix(params, "&")
} }
c.Redirect(http.StatusTemporaryRedirect, redirectURL) c.Redirect(http.StatusTemporaryRedirect, redirectURL)
@@ -283,6 +285,10 @@ func processGithubUserInfo(code string) (models.User, error) {
log.Debug("Failed to read github user info response body: ", err) log.Debug("Failed to read github user info response body: ", err)
return user, fmt.Errorf("failed to read github response body: %s", err.Error()) return user, fmt.Errorf("failed to read github response body: %s", err.Error())
} }
if response.StatusCode >= 400 {
log.Debug("Failed to request linkedin user info: ", string(body))
return user, fmt.Errorf("failed to request linkedin user info: %s", string(body))
}
userRawData := make(map[string]string) userRawData := make(map[string]string)
json.Unmarshal(body, &userRawData) json.Unmarshal(body, &userRawData)
@@ -335,7 +341,10 @@ func processFacebookUserInfo(code string) (models.User, error) {
log.Debug("Failed to read facebook response: ", err) log.Debug("Failed to read facebook response: ", err)
return user, fmt.Errorf("failed to read facebook response body: %s", err.Error()) return user, fmt.Errorf("failed to read facebook response body: %s", err.Error())
} }
if response.StatusCode >= 400 {
log.Debug("Failed to request linkedin user info: ", string(body))
return user, fmt.Errorf("failed to request linkedin user info: %s", string(body))
}
userRawData := make(map[string]interface{}) userRawData := make(map[string]interface{})
json.Unmarshal(body, &userRawData) json.Unmarshal(body, &userRawData)
@@ -356,3 +365,85 @@ func processFacebookUserInfo(code string) (models.User, error) {
return user, nil return user, nil
} }
func processLinkedInUserInfo(code string) (models.User, error) {
user := models.User{}
token, err := oauth.OAuthProviders.LinkedInConfig.Exchange(oauth2.NoContext, code)
if err != nil {
log.Debug("Failed to exchange code for token: ", err)
return user, fmt.Errorf("invalid linkedin exchange code: %s", err.Error())
}
client := http.Client{}
req, err := http.NewRequest("GET", constants.LinkedInUserInfoURL, nil)
if err != nil {
log.Debug("Failed to create linkedin user info request: ", err)
return user, fmt.Errorf("error creating linkedin user info request: %s", err.Error())
}
req.Header = http.Header{
"Authorization": []string{fmt.Sprintf("Bearer %s", token.AccessToken)},
}
response, err := client.Do(req)
if err != nil {
log.Debug("Failed to request linkedin user info: ", err)
return user, err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Debug("Failed to read linkedin user info response body: ", err)
return user, fmt.Errorf("failed to read linkedin response body: %s", err.Error())
}
if response.StatusCode >= 400 {
log.Debug("Failed to request linkedin user info: ", string(body))
return user, fmt.Errorf("failed to request linkedin user info: %s", string(body))
}
userRawData := make(map[string]interface{})
json.Unmarshal(body, &userRawData)
req, err = http.NewRequest("GET", constants.LinkedInEmailURL, nil)
if err != nil {
log.Debug("Failed to create linkedin email info request: ", err)
return user, fmt.Errorf("error creating linkedin user info request: %s", err.Error())
}
req.Header = http.Header{
"Authorization": []string{fmt.Sprintf("Bearer %s", token.AccessToken)},
}
response, err = client.Do(req)
if err != nil {
log.Debug("Failed to request linkedin email info: ", err)
return user, err
}
defer response.Body.Close()
body, err = ioutil.ReadAll(response.Body)
if err != nil {
log.Debug("Failed to read linkedin email info response body: ", err)
return user, fmt.Errorf("failed to read linkedin email response body: %s", err.Error())
}
if response.StatusCode >= 400 {
log.Debug("Failed to request linkedin user info: ", string(body))
return user, fmt.Errorf("failed to request linkedin user info: %s", string(body))
}
emailRawData := make(map[string]interface{})
json.Unmarshal(body, &emailRawData)
firstName := userRawData["localizedFirstName"].(string)
lastName := userRawData["localizedLastName"].(string)
profilePicture := userRawData["profilePicture"].(map[string]interface{})["displayImage~"].(map[string]interface{})["elements"].([]interface{})[0].(map[string]interface{})["identifiers"].([]interface{})[0].(map[string]interface{})["identifier"].(string)
emailAddress := emailRawData["elements"].([]interface{})[0].(map[string]interface{})["handle~"].(map[string]interface{})["emailAddress"].(string)
user = models.User{
GivenName: &firstName,
FamilyName: &lastName,
Picture: &profilePicture,
Email: emailAddress,
}
return user, nil
}

View File

@@ -151,6 +151,23 @@ func OAuthLoginHandler() gin.HandlerFunc {
oauth.OAuthProviders.FacebookConfig.RedirectURL = hostname + "/oauth_callback/facebook" oauth.OAuthProviders.FacebookConfig.RedirectURL = hostname + "/oauth_callback/facebook"
url := oauth.OAuthProviders.FacebookConfig.AuthCodeURL(oauthStateString) url := oauth.OAuthProviders.FacebookConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url) c.Redirect(http.StatusTemporaryRedirect, url)
case constants.SignupMethodLinkedIn:
if oauth.OAuthProviders.LinkedInConfig == nil {
log.Debug("Linkedin OAuth provider is not configured")
isProviderConfigured = false
break
}
err := memorystore.Provider.SetState(oauthStateString, constants.SignupMethodLinkedIn)
if err != nil {
log.Debug("Error setting state: ", err)
c.JSON(500, gin.H{
"error": "internal server error",
})
return
}
oauth.OAuthProviders.LinkedInConfig.RedirectURL = hostname + "/oauth_callback/linkedin"
url := oauth.OAuthProviders.LinkedInConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url)
default: default:
log.Debug("Invalid oauth provider: ", provider) log.Debug("Invalid oauth provider: ", provider)
c.JSON(422, gin.H{ c.JSON(422, gin.H{

View File

@@ -42,7 +42,6 @@ func VerifyEmailHandler() gin.HandlerFunc {
// verify if token exists in db // verify if token exists in db
hostname := parsers.GetHost(c) hostname := parsers.GetHost(c)
log.Debug("hostname used for jwt verification: ", hostname)
claim, err := token.ParseJWTToken(tokenInQuery, hostname, verificationRequest.Nonce, verificationRequest.Email) claim, err := token.ParseJWTToken(tokenInQuery, hostname, verificationRequest.Nonce, verificationRequest.Email)
if err != nil { if err != nil {
log.Debug("Error parsing token: ", err) log.Debug("Error parsing token: ", err)
@@ -116,7 +115,7 @@ func VerifyEmailHandler() gin.HandlerFunc {
if strings.Contains(redirectURL, "?") { if strings.Contains(redirectURL, "?") {
redirectURL = redirectURL + "&" + params redirectURL = redirectURL + "&" + params
} else { } else {
redirectURL = redirectURL + "?" + params redirectURL = redirectURL + "?" + strings.TrimPrefix(params, "&")
} }
go db.Provider.AddSession(models.Session{ go db.Provider.AddSession(models.Session{

View File

@@ -3,6 +3,8 @@ package inmemory
import ( import (
"os" "os"
"sync" "sync"
"github.com/authorizerdev/authorizer/server/constants"
) )
// EnvStore struct to store the env variables // EnvStore struct to store the env variables
@@ -13,7 +15,7 @@ type EnvStore struct {
// UpdateEnvStore to update the whole env store object // UpdateEnvStore to update the whole env store object
func (e *EnvStore) UpdateStore(store map[string]interface{}) { func (e *EnvStore) UpdateStore(store map[string]interface{}) {
if os.Getenv("ENV") != "test" { if os.Getenv("ENV") != constants.TestEnv {
e.mutex.Lock() e.mutex.Lock()
defer e.mutex.Unlock() defer e.mutex.Unlock()
} }
@@ -26,26 +28,17 @@ func (e *EnvStore) UpdateStore(store map[string]interface{}) {
// GetStore returns the env store // GetStore returns the env store
func (e *EnvStore) GetStore() map[string]interface{} { func (e *EnvStore) GetStore() map[string]interface{} {
if os.Getenv("ENV") != "test" {
e.mutex.Lock()
defer e.mutex.Unlock()
}
return e.store return e.store
} }
// Get returns the value of the key in evn store // Get returns the value of the key in evn store
func (e *EnvStore) Get(key string) interface{} { func (e *EnvStore) Get(key string) interface{} {
if os.Getenv("ENV") != "test" {
e.mutex.Lock()
defer e.mutex.Unlock()
}
return e.store[key] return e.store[key]
} }
// Set sets the value of the key in env store // Set sets the value of the key in env store
func (e *EnvStore) Set(key string, value interface{}) { func (e *EnvStore) Set(key string, value interface{}) {
if os.Getenv("ENV") != "test" { if os.Getenv("ENV") != constants.TestEnv {
e.mutex.Lock() e.mutex.Lock()
defer e.mutex.Unlock() defer e.mutex.Unlock()
} }

View File

@@ -4,11 +4,13 @@ import (
"fmt" "fmt"
"os" "os"
"strings" "strings"
"github.com/authorizerdev/authorizer/server/constants"
) )
// ClearStore clears the in-memory store. // ClearStore clears the in-memory store.
func (c *provider) ClearStore() error { func (c *provider) ClearStore() error {
if os.Getenv("ENV") != "test" { if os.Getenv("ENV") != constants.TestEnv {
c.mutex.Lock() c.mutex.Lock()
defer c.mutex.Unlock() defer c.mutex.Unlock()
} }
@@ -19,10 +21,6 @@ func (c *provider) ClearStore() error {
// GetUserSessions returns all the user session token from the in-memory store. // GetUserSessions returns all the user session token from the in-memory store.
func (c *provider) GetUserSessions(userId string) map[string]string { func (c *provider) GetUserSessions(userId string) map[string]string {
if os.Getenv("ENV") != "test" {
c.mutex.Lock()
defer c.mutex.Unlock()
}
res := map[string]string{} res := map[string]string{}
for k, v := range c.stateStore { for k, v := range c.stateStore {
split := strings.Split(v, "@") split := strings.Split(v, "@")
@@ -36,7 +34,7 @@ func (c *provider) GetUserSessions(userId string) map[string]string {
// DeleteAllUserSession deletes all the user sessions from in-memory store. // DeleteAllUserSession deletes all the user sessions from in-memory store.
func (c *provider) DeleteAllUserSession(userId string) error { func (c *provider) DeleteAllUserSession(userId string) error {
if os.Getenv("ENV") != "test" { if os.Getenv("ENV") != constants.TestEnv {
c.mutex.Lock() c.mutex.Lock()
defer c.mutex.Unlock() defer c.mutex.Unlock()
} }
@@ -50,7 +48,7 @@ func (c *provider) DeleteAllUserSession(userId string) error {
// SetState sets the state in the in-memory store. // SetState sets the state in the in-memory store.
func (c *provider) SetState(key, state string) error { func (c *provider) SetState(key, state string) error {
if os.Getenv("ENV") != "test" { if os.Getenv("ENV") != constants.TestEnv {
c.mutex.Lock() c.mutex.Lock()
defer c.mutex.Unlock() defer c.mutex.Unlock()
} }
@@ -61,11 +59,6 @@ func (c *provider) SetState(key, state string) error {
// GetState gets the state from the in-memory store. // GetState gets the state from the in-memory store.
func (c *provider) GetState(key string) (string, error) { func (c *provider) GetState(key string) (string, error) {
if os.Getenv("ENV") != "test" {
c.mutex.Lock()
defer c.mutex.Unlock()
}
state := "" state := ""
if stateVal, ok := c.stateStore[key]; ok { if stateVal, ok := c.stateStore[key]; ok {
state = stateVal state = stateVal
@@ -76,7 +69,7 @@ func (c *provider) GetState(key string) (string, error) {
// RemoveState removes the state from the in-memory store. // RemoveState removes the state from the in-memory store.
func (c *provider) RemoveState(key string) error { func (c *provider) RemoveState(key string) error {
if os.Getenv("ENV") != "test" { if os.Getenv("ENV") != constants.TestEnv {
c.mutex.Lock() c.mutex.Lock()
defer c.mutex.Unlock() defer c.mutex.Unlock()
} }

View File

@@ -7,6 +7,7 @@ import (
"golang.org/x/oauth2" "golang.org/x/oauth2"
facebookOAuth2 "golang.org/x/oauth2/facebook" facebookOAuth2 "golang.org/x/oauth2/facebook"
githubOAuth2 "golang.org/x/oauth2/github" githubOAuth2 "golang.org/x/oauth2/github"
linkedInOAuth2 "golang.org/x/oauth2/linkedin"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/memorystore" "github.com/authorizerdev/authorizer/server/memorystore"
@@ -17,6 +18,7 @@ type OAuthProvider struct {
GoogleConfig *oauth2.Config GoogleConfig *oauth2.Config
GithubConfig *oauth2.Config GithubConfig *oauth2.Config
FacebookConfig *oauth2.Config FacebookConfig *oauth2.Config
LinkedInConfig *oauth2.Config
} }
// OIDCProviders is a struct that contains reference all the OpenID providers // OIDCProviders is a struct that contains reference all the OpenID providers
@@ -92,5 +94,23 @@ func InitOAuth() error {
} }
} }
linkedInClientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyLinkedInClientID)
if err != nil {
linkedInClientID = ""
}
linkedInClientSecret, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyLinkedInClientSecret)
if err != nil {
linkedInClientSecret = ""
}
if linkedInClientID != "" && linkedInClientSecret != "" {
OAuthProviders.LinkedInConfig = &oauth2.Config{
ClientID: linkedInClientID,
ClientSecret: linkedInClientSecret,
RedirectURL: "/oauth_callback/linkedin",
Endpoint: linkedInOAuth2.Endpoint,
Scopes: []string{"r_liteprofile", "r_emailaddress"},
}
}
return nil return nil
} }

View File

@@ -4,9 +4,10 @@ import (
"net/url" "net/url"
"strings" "strings"
"github.com/gin-gonic/gin"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/memorystore" "github.com/authorizerdev/authorizer/server/memorystore"
"github.com/gin-gonic/gin"
) )
// GetHost returns hostname from request context // GetHost returns hostname from request context
@@ -14,15 +15,15 @@ import (
// if EnvKeyAuthorizerURL is set it is given second highest priority. // if EnvKeyAuthorizerURL is set it is given second highest priority.
// if above 2 are not set the requesting host name is used // if above 2 are not set the requesting host name is used
func GetHost(c *gin.Context) string { func GetHost(c *gin.Context) string {
authorizerURL := c.Request.Header.Get("X-Authorizer-URL") authorizerURL, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL)
if err != nil {
authorizerURL = ""
}
if authorizerURL != "" { if authorizerURL != "" {
return authorizerURL return authorizerURL
} }
authorizerURL, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) authorizerURL = c.Request.Header.Get("X-Authorizer-URL")
if err == nil {
authorizerURL = ""
}
if authorizerURL != "" { if authorizerURL != "" {
return authorizerURL return authorizerURL
} }

View File

@@ -130,6 +130,12 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
if val, ok := store[constants.EnvKeyGithubClientSecret]; ok { if val, ok := store[constants.EnvKeyGithubClientSecret]; ok {
res.GithubClientSecret = utils.NewStringRef(val.(string)) res.GithubClientSecret = utils.NewStringRef(val.(string))
} }
if val, ok := store[constants.EnvKeyLinkedInClientID]; ok {
res.LinkedinClientID = utils.NewStringRef(val.(string))
}
if val, ok := store[constants.EnvKeyLinkedInClientSecret]; ok {
res.LinkedinClientSecret = utils.NewStringRef(val.(string))
}
if val, ok := store[constants.EnvKeyOrganizationName]; ok { if val, ok := store[constants.EnvKeyOrganizationName]; ok {
res.OrganizationName = utils.NewStringRef(val.(string)) res.OrganizationName = utils.NewStringRef(val.(string))
} }
@@ -141,7 +147,14 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
res.AllowedOrigins = strings.Split(store[constants.EnvKeyAllowedOrigins].(string), ",") res.AllowedOrigins = strings.Split(store[constants.EnvKeyAllowedOrigins].(string), ",")
res.Roles = strings.Split(store[constants.EnvKeyRoles].(string), ",") res.Roles = strings.Split(store[constants.EnvKeyRoles].(string), ",")
res.DefaultRoles = strings.Split(store[constants.EnvKeyDefaultRoles].(string), ",") res.DefaultRoles = strings.Split(store[constants.EnvKeyDefaultRoles].(string), ",")
res.ProtectedRoles = strings.Split(store[constants.EnvKeyProtectedRoles].(string), ",") // since protected role is optional default split gives array with empty string
protectedRoles := strings.Split(store[constants.EnvKeyProtectedRoles].(string), ",")
res.ProtectedRoles = []string{}
for _, role := range protectedRoles {
if strings.Trim(role, " ") != "" {
res.ProtectedRoles = append(res.ProtectedRoles, strings.Trim(role, " "))
}
}
// bool vars // bool vars
res.DisableEmailVerification = store[constants.EnvKeyDisableEmailVerification].(bool) res.DisableEmailVerification = store[constants.EnvKeyDisableEmailVerification].(bool)

View File

@@ -197,7 +197,7 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
if strings.Contains(redirectURL, "?") { if strings.Contains(redirectURL, "?") {
redirectURL = redirectURL + "&" + redirectURLParams redirectURL = redirectURL + "&" + redirectURLParams
} else { } else {
redirectURL = redirectURL + "?" + redirectURLParams redirectURL = redirectURL + "?" + strings.TrimPrefix(redirectURLParams, "&")
} }
verificationType := constants.VerificationTypeMagicLinkLogin verificationType := constants.VerificationTypeMagicLinkLogin

View File

@@ -41,6 +41,18 @@ func MetaResolver(ctx context.Context) (*model.Meta, error) {
facebookClientSecret = "" facebookClientSecret = ""
} }
linkedClientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyLinkedInClientID)
if err != nil {
log.Debug("Failed to get Facebook Client ID from environment variable", err)
linkedClientID = ""
}
linkedInClientSecret, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyLinkedInClientSecret)
if err != nil {
log.Debug("Failed to get Facebook Client Secret from environment variable", err)
linkedInClientSecret = ""
}
githubClientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyGithubClientID) githubClientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyGithubClientID)
if err != nil { if err != nil {
log.Debug("Failed to get Github Client ID from environment variable", err) log.Debug("Failed to get Github Client ID from environment variable", err)
@@ -83,6 +95,7 @@ func MetaResolver(ctx context.Context) (*model.Meta, error) {
IsGoogleLoginEnabled: googleClientID != "" && googleClientSecret != "", IsGoogleLoginEnabled: googleClientID != "" && googleClientSecret != "",
IsGithubLoginEnabled: githubClientID != "" && githubClientSecret != "", IsGithubLoginEnabled: githubClientID != "" && githubClientSecret != "",
IsFacebookLoginEnabled: facebookClientID != "" && facebookClientSecret != "", IsFacebookLoginEnabled: facebookClientID != "" && facebookClientSecret != "",
IsLinkedinLoginEnabled: linkedClientID != "" && linkedInClientSecret != "",
IsBasicAuthenticationEnabled: !isBasicAuthDisabled, IsBasicAuthenticationEnabled: !isBasicAuthDisabled,
IsEmailVerificationEnabled: !isEmailVerificationDisabled, IsEmailVerificationEnabled: !isEmailVerificationDisabled,
IsMagicLinkLoginEnabled: !isMagicLinkLoginDisabled, IsMagicLinkLoginEnabled: !isMagicLinkLoginDisabled,

View File

@@ -100,7 +100,7 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
} else { } else {
roles = strings.Split(rolesString, ",") roles = strings.Split(rolesString, ",")
} }
if !validators.IsValidRoles(roles, params.Roles) { if !validators.IsValidRoles(params.Roles, roles) {
log.Debug("Invalid roles: ", params.Roles) log.Debug("Invalid roles: ", params.Roles)
return res, fmt.Errorf(`invalid roles`) return res, fmt.Errorf(`invalid roles`)
} else { } else {

View File

@@ -4,7 +4,6 @@ import (
"errors" "errors"
"github.com/golang-jwt/jwt" "github.com/golang-jwt/jwt"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/crypto" "github.com/authorizerdev/authorizer/server/crypto"
@@ -117,7 +116,6 @@ func ParseJWTToken(token, hostname, nonce, subject string) (jwt.MapClaims, error
intIat := int64(claims["iat"].(float64)) intIat := int64(claims["iat"].(float64))
claims["exp"] = intExp claims["exp"] = intExp
claims["iat"] = intIat claims["iat"] = intIat
log.Debug("claims: ", claims)
clientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyClientID) clientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyClientID)
if err != nil { if err != nil {
return claims, err return claims, err
@@ -199,7 +197,6 @@ func ParseJWTTokenWithoutNonce(token, hostname string) (jwt.MapClaims, error) {
intIat := int64(claims["iat"].(float64)) intIat := int64(claims["iat"].(float64))
claims["exp"] = intExp claims["exp"] = intExp
claims["iat"] = intIat claims["iat"] = intIat
log.Debug("claims: ", claims)
clientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyClientID) clientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyClientID)
if err != nil { if err != nil {
return claims, err return claims, err