mirror of
https://github.com/wassname/talk.git
synced 2026-08-01 13:00:55 +08:00
[next] Auth Popup v2 (#2101)
* feat: Implement new Sign In view * feat: Move forgot + resetPassword to new design * feat: Implement sign up with new design * fix: narrow gutter * test: add unit tests * test: integration tests * feat: support show / hide password * feat: support oauth2 flow * feat: add views for user completion * feat: implement oauth2 sign up * test: fix snapshots * fix: lint * fix: get more complete mutation response * fix: removed array of OIDC integrations * fix: renamed resolver function * fix: adapt oidc client implementation * fix: targetFilter should be stream on signup * fix: removed unneeded message * fix: moved password into local profile * fix: made username optional, removed valid null value * fix: linting * fix: respect targetFilter * feat: support user registration mutations - Added `setUsername` - Added `setEmail` - Added `setPassword` - Added `permit` to `@auth` - Added `email` to `User` * fix: fixed issue with query * feat: added user password update * feat: complete sign in mutation * fix: adapt some rebasing gitches * test: improve tests * test: unittest for setting auth token * fix: failing tests * test: move most tests from enzyme to react-test-renderer * fix: remove schema warnings in tests * test: improve window mock * test: test different social login configurations * test: test social logins for sign up * fix: use htmlFor instead of for * test: more feature tests * feat: always go through account completion * test: feature test account completion * feat: addtional account completion test * Update start.ts * chore: refactor auth token retrieval logic
This commit is contained in:
@@ -1,19 +1,67 @@
|
||||
import { DirectiveResolverFn } from "graphql-tools";
|
||||
import { memoize } from "lodash";
|
||||
|
||||
import CommonContext from "talk-server/graph/common/context";
|
||||
import { GQLUSER_ROLE } from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
import {
|
||||
GQLUSER_AUTH_CONDITIONS,
|
||||
GQLUSER_ROLE,
|
||||
} from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
import { User } from "talk-server/models/user";
|
||||
|
||||
// Replace `memoize.Cache`.
|
||||
memoize.Cache = WeakMap;
|
||||
|
||||
export interface AuthDirectiveArgs {
|
||||
roles?: GQLUSER_ROLE[];
|
||||
userIDField?: string;
|
||||
permit?: GQLUSER_AUTH_CONDITIONS[];
|
||||
}
|
||||
|
||||
function calculateAuthConditions(user: User): GQLUSER_AUTH_CONDITIONS[] {
|
||||
const conditions: GQLUSER_AUTH_CONDITIONS[] = [];
|
||||
|
||||
if (!user.username && !user.displayName) {
|
||||
conditions.push(GQLUSER_AUTH_CONDITIONS.MISSING_NAME);
|
||||
}
|
||||
|
||||
if (!user.email) {
|
||||
conditions.push(GQLUSER_AUTH_CONDITIONS.MISSING_EMAIL);
|
||||
}
|
||||
|
||||
return conditions.sort();
|
||||
}
|
||||
|
||||
const calculateAuthConditionsMemoized = memoize(calculateAuthConditions);
|
||||
|
||||
const auth: DirectiveResolverFn<
|
||||
Record<string, string | undefined>,
|
||||
CommonContext
|
||||
> = (next, src, { roles, userIDField }: AuthDirectiveArgs, { user }) => {
|
||||
> = (
|
||||
next,
|
||||
src,
|
||||
{ roles, userIDField, permit }: AuthDirectiveArgs,
|
||||
{ user }
|
||||
) => {
|
||||
// If there is a user on the request.
|
||||
if (user) {
|
||||
// If the permit was not specified, then no conditions can exist on the
|
||||
// User, if they do error.
|
||||
const conditions = calculateAuthConditionsMemoized(user);
|
||||
if (!permit && conditions.length > 0) {
|
||||
// TODO: return better error.
|
||||
throw new Error("not authorized");
|
||||
}
|
||||
|
||||
// If the permit was specified, and some of the conditions for the user
|
||||
// aren't in the list of permitted conditions, then error.
|
||||
if (
|
||||
permit &&
|
||||
conditions.some(condition => permit.indexOf(condition) === -1)
|
||||
) {
|
||||
// TODO: return better error.
|
||||
throw new Error("not authorized");
|
||||
}
|
||||
|
||||
// If the role and user owner checks are disabled, then allow them based on
|
||||
// their authenticated status.
|
||||
if (!roles && !userIDField) {
|
||||
|
||||
@@ -1,20 +1,9 @@
|
||||
import { isNull, omitBy } from "lodash";
|
||||
|
||||
import TenantContext from "talk-server/graph/tenant/context";
|
||||
import {
|
||||
GQLCreateOIDCAuthIntegrationInput,
|
||||
GQLRemoveOIDCAuthIntegrationInput,
|
||||
GQLSettingsInput,
|
||||
GQLUpdateOIDCAuthIntegrationInput,
|
||||
} from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
import { GQLSettingsInput } from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
import { Tenant } from "talk-server/models/tenant";
|
||||
import {
|
||||
createOIDCAuthIntegration,
|
||||
regenerateSSOKey,
|
||||
removeOIDCAuthIntegration,
|
||||
update,
|
||||
updateOIDCAuthIntegration,
|
||||
} from "talk-server/services/tenant";
|
||||
import { regenerateSSOKey, update } from "talk-server/services/tenant";
|
||||
|
||||
export const Settings = ({
|
||||
mongo,
|
||||
@@ -26,23 +15,4 @@ export const Settings = ({
|
||||
update(mongo, redis, tenantCache, tenant, omitBy(input, isNull)),
|
||||
regenerateSSOKey: (): Promise<Tenant | null> =>
|
||||
regenerateSSOKey(mongo, redis, tenantCache, tenant),
|
||||
createOIDCAuthIntegration: (input: GQLCreateOIDCAuthIntegrationInput) =>
|
||||
createOIDCAuthIntegration(
|
||||
mongo,
|
||||
redis,
|
||||
tenantCache,
|
||||
tenant,
|
||||
input.configuration
|
||||
),
|
||||
updateOIDCAuthIntegration: (input: GQLUpdateOIDCAuthIntegrationInput) =>
|
||||
updateOIDCAuthIntegration(
|
||||
mongo,
|
||||
redis,
|
||||
tenantCache,
|
||||
tenant,
|
||||
input.id,
|
||||
input.configuration
|
||||
),
|
||||
removeOIDCAuthIntegration: (input: GQLRemoveOIDCAuthIntegrationInput) =>
|
||||
removeOIDCAuthIntegration(mongo, redis, tenantCache, tenant, input.id),
|
||||
});
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import TenantContext from "talk-server/graph/tenant/context";
|
||||
import * as user from "talk-server/models/user";
|
||||
import {
|
||||
setEmail,
|
||||
setPassword,
|
||||
setUsername,
|
||||
updatePassword,
|
||||
} from "talk-server/services/users";
|
||||
import {
|
||||
GQLSetEmailInput,
|
||||
GQLSetPasswordInput,
|
||||
GQLSetUsernameInput,
|
||||
GQLUpdatePasswordInput,
|
||||
} from "../schema/__generated__/types";
|
||||
|
||||
export const User = (ctx: TenantContext) => ({
|
||||
setUsername: async (
|
||||
input: GQLSetUsernameInput
|
||||
): Promise<Readonly<user.User> | null> =>
|
||||
setUsername(ctx.mongo, ctx.tenant, ctx.user!, input.username),
|
||||
setEmail: async (
|
||||
input: GQLSetEmailInput
|
||||
): Promise<Readonly<user.User> | null> =>
|
||||
setEmail(ctx.mongo, ctx.tenant, ctx.user!, input.email),
|
||||
setPassword: async (
|
||||
input: GQLSetPasswordInput
|
||||
): Promise<Readonly<user.User> | null> =>
|
||||
setPassword(ctx.mongo, ctx.tenant, ctx.user!, input.password),
|
||||
updatePassword: async (
|
||||
input: GQLUpdatePasswordInput
|
||||
): Promise<Readonly<user.User> | null> =>
|
||||
updatePassword(ctx.mongo, ctx.tenant, ctx.user!, input.password),
|
||||
});
|
||||
@@ -4,10 +4,12 @@ import { Actions } from "./Actions";
|
||||
import { Comment } from "./Comment";
|
||||
import { Settings } from "./Settings";
|
||||
import { Story } from "./Story";
|
||||
import { User } from "./User";
|
||||
|
||||
export default (ctx: TenantContext) => ({
|
||||
Actions: Actions(ctx),
|
||||
Comment: Comment(ctx),
|
||||
Settings: Settings(ctx),
|
||||
Story: Story(ctx),
|
||||
User: User(ctx),
|
||||
});
|
||||
|
||||
@@ -1,24 +1,15 @@
|
||||
import { constructTenantURL, reconstructURL } from "talk-server/app/url";
|
||||
import {
|
||||
GQLFacebookAuthIntegration,
|
||||
GQLFacebookAuthIntegrationTypeResolver,
|
||||
} from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
|
||||
import { reconstructTenantURLResolver } from "./util";
|
||||
|
||||
export const FacebookAuthIntegration: GQLFacebookAuthIntegrationTypeResolver<
|
||||
GQLFacebookAuthIntegration
|
||||
> = {
|
||||
callbackURL: (integration, args, ctx) => {
|
||||
const path = `/api/tenant/auth/facebook/callback`;
|
||||
|
||||
// If the request is available, then prefer it over building from the tenant
|
||||
// as the tenant does not include the port number. This should only really
|
||||
// be a problem if the graph API is called internally.
|
||||
if (ctx.req) {
|
||||
return reconstructURL(ctx.req, path);
|
||||
}
|
||||
|
||||
// Note that when constructing the callback url with the tenant, the port
|
||||
// information is lost.
|
||||
return constructTenantURL(ctx.config, ctx.tenant, path);
|
||||
},
|
||||
callbackURL: reconstructTenantURLResolver(
|
||||
"/api/tenant/auth/facebook/callback"
|
||||
),
|
||||
redirectURL: reconstructTenantURLResolver("/api/tenant/auth/facebook"),
|
||||
};
|
||||
|
||||
@@ -1,24 +1,13 @@
|
||||
import { constructTenantURL, reconstructURL } from "talk-server/app/url";
|
||||
import {
|
||||
GQLGoogleAuthIntegration,
|
||||
GQLGoogleAuthIntegrationTypeResolver,
|
||||
} from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
|
||||
import { reconstructTenantURLResolver } from "./util";
|
||||
|
||||
export const GoogleAuthIntegration: GQLGoogleAuthIntegrationTypeResolver<
|
||||
GQLGoogleAuthIntegration
|
||||
> = {
|
||||
callbackURL: (integration, args, ctx) => {
|
||||
const path = `/api/tenant/auth/google/callback`;
|
||||
|
||||
// If the request is available, then prefer it over building from the tenant
|
||||
// as the tenant does not include the port number. This should only really
|
||||
// be a problem if the graph API is called internally.
|
||||
if (ctx.req) {
|
||||
return reconstructURL(ctx.req, path);
|
||||
}
|
||||
|
||||
// Note that when constructing the callback url with the tenant, the port
|
||||
// information is lost.
|
||||
return constructTenantURL(ctx.config, ctx.tenant, path);
|
||||
},
|
||||
callbackURL: reconstructTenantURLResolver("/api/tenant/auth/google/callback"),
|
||||
redirectURL: reconstructTenantURLResolver("/api/tenant/auth/google"),
|
||||
};
|
||||
|
||||
@@ -53,42 +53,6 @@ export const Mutation: Required<GQLMutationTypeResolver<void>> = {
|
||||
settings: await ctx.mutators.Settings.regenerateSSOKey(),
|
||||
clientMutationId: input.clientMutationId,
|
||||
}),
|
||||
createOIDCAuthIntegration: async (source, { input }, ctx) => {
|
||||
const result = await ctx.mutators.Settings.createOIDCAuthIntegration(input);
|
||||
if (!result) {
|
||||
return { clientMutationId: input.clientMutationId };
|
||||
}
|
||||
|
||||
return {
|
||||
integration: result.integration,
|
||||
settings: result.tenant,
|
||||
clientMutationId: input.clientMutationId,
|
||||
};
|
||||
},
|
||||
updateOIDCAuthIntegration: async (source, { input }, ctx) => {
|
||||
const result = await ctx.mutators.Settings.updateOIDCAuthIntegration(input);
|
||||
if (!result) {
|
||||
return { clientMutationId: input.clientMutationId };
|
||||
}
|
||||
|
||||
return {
|
||||
integration: result.integration,
|
||||
settings: result.tenant,
|
||||
clientMutationId: input.clientMutationId,
|
||||
};
|
||||
},
|
||||
removeOIDCAuthIntegration: async (source, { input }, ctx) => {
|
||||
const result = await ctx.mutators.Settings.removeOIDCAuthIntegration(input);
|
||||
if (!result) {
|
||||
return { clientMutationId: input.clientMutationId };
|
||||
}
|
||||
|
||||
return {
|
||||
integration: result.integration,
|
||||
settings: result.tenant,
|
||||
clientMutationId: input.clientMutationId,
|
||||
};
|
||||
},
|
||||
createStory: async (source, { input }, ctx) => ({
|
||||
story: await ctx.mutators.Story.create(input),
|
||||
clientMutationId: input.clientMutationId,
|
||||
@@ -117,4 +81,20 @@ export const Mutation: Required<GQLMutationTypeResolver<void>> = {
|
||||
comment: await ctx.mutators.Actions.rejectComment(input),
|
||||
clientMutationId: input.clientMutationId,
|
||||
}),
|
||||
setUsername: async (source, { input }, ctx) => ({
|
||||
user: await ctx.mutators.User.setUsername(input),
|
||||
clientMutationId: input.clientMutationId,
|
||||
}),
|
||||
setEmail: async (source, { input }, ctx) => ({
|
||||
user: await ctx.mutators.User.setEmail(input),
|
||||
clientMutationId: input.clientMutationId,
|
||||
}),
|
||||
setPassword: async (source, { input }, ctx) => ({
|
||||
user: await ctx.mutators.User.setPassword(input),
|
||||
clientMutationId: input.clientMutationId,
|
||||
}),
|
||||
updatePassword: async (source, { input }, ctx) => ({
|
||||
user: await ctx.mutators.User.updatePassword(input),
|
||||
clientMutationId: input.clientMutationId,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -1,24 +1,13 @@
|
||||
import { constructTenantURL, reconstructURL } from "talk-server/app/url";
|
||||
import {
|
||||
GQLOIDCAuthIntegration,
|
||||
GQLOIDCAuthIntegrationTypeResolver,
|
||||
} from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
|
||||
import { reconstructTenantURLResolver } from "./util";
|
||||
|
||||
export const OIDCAuthIntegration: GQLOIDCAuthIntegrationTypeResolver<
|
||||
GQLOIDCAuthIntegration
|
||||
> = {
|
||||
callbackURL: (integration, args, ctx) => {
|
||||
const path = `/api/tenant/auth/oidc/${integration.id}`;
|
||||
|
||||
// If the request is available, then prefer it over building from the tenant
|
||||
// as the tenant does not include the port number. This should only really
|
||||
// be a problem if the graph API is called internally.
|
||||
if (ctx.req) {
|
||||
return reconstructURL(ctx.req, path);
|
||||
}
|
||||
|
||||
// Note that when constructing the callback url with the tenant, the port
|
||||
// information is lost.
|
||||
return constructTenantURL(ctx.config, ctx.tenant, path);
|
||||
},
|
||||
callbackURL: reconstructTenantURLResolver("/api/tenant/auth/oidc/callback"),
|
||||
redirectURL: reconstructTenantURLResolver("/api/tenant/auth/oidc"),
|
||||
};
|
||||
|
||||
@@ -10,6 +10,10 @@ const resolveType: GQLProfileTypeResolver<user.Profile> = profile => {
|
||||
return "OIDCProfile";
|
||||
case "sso":
|
||||
return "SSOProfile";
|
||||
case "facebook":
|
||||
return "FacebookProfile";
|
||||
case "google":
|
||||
return "GoogleProfile";
|
||||
default:
|
||||
// TODO: replace with better error.
|
||||
throw new Error("invalid profile type");
|
||||
|
||||
@@ -4,6 +4,10 @@ import { pull } from "lodash";
|
||||
import { parseQuery, stringifyQuery } from "talk-common/utils";
|
||||
import { URL } from "url";
|
||||
|
||||
import { constructTenantURL, reconstructURL } from "talk-server/app/url";
|
||||
|
||||
import TenantContext from "../context";
|
||||
|
||||
/**
|
||||
* getRequestedFields returns the fields in an array that are being queried for.
|
||||
*
|
||||
@@ -26,3 +30,18 @@ export function getURLWithCommentID(storyURL: string, commentID?: string) {
|
||||
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
export function reconstructTenantURLResolver<T = any>(path: string) {
|
||||
return (parent: T, args: {}, ctx: TenantContext) => {
|
||||
// If the request is available, then prefer it over building from the tenant
|
||||
// as the tenant does not include the port number. This should only really
|
||||
// be a problem if the graph API is called internally.
|
||||
if (ctx.req) {
|
||||
return reconstructURL(ctx.req, path);
|
||||
}
|
||||
|
||||
// Note that when constructing the callback url with the tenant, the port
|
||||
// information is lost.
|
||||
return constructTenantURL(ctx.config, ctx.tenant, path);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,15 +2,41 @@
|
||||
## Custom Directives
|
||||
################################################################################
|
||||
|
||||
"""
|
||||
USER_AUTH_CONDITIONS describes conditions that would prevent a given User to
|
||||
execute any set of mutations or reserved queries.
|
||||
"""
|
||||
enum USER_AUTH_CONDITIONS {
|
||||
"""
|
||||
MISSING_NAME is provided when the User does not have an associated username or
|
||||
display name.
|
||||
"""
|
||||
MISSING_NAME
|
||||
|
||||
"""
|
||||
MISSING_EMAIL is provided when the User does not have an associated email
|
||||
address.
|
||||
"""
|
||||
MISSING_EMAIL
|
||||
}
|
||||
|
||||
"""
|
||||
auth is a directive that will enforce authorization rules on the schema
|
||||
definition. It will restrict the viewer of the field based on roles or if the
|
||||
`userIDField` is specified, it will see if the current users ID equals the field
|
||||
specified. This allows users that own a specific resource (like a comment, or a
|
||||
flag) see their own content, but restrict it to everyone else. If the directive
|
||||
is used without options, it simply requires a logged in user.
|
||||
is used without options, it simply requires a logged in user. `permit` can be
|
||||
used to allow specific `USER_AUTH_CONDITIONS` that normally (if present) would
|
||||
deny access to any edge associated with the `@auth` directive. If a User has
|
||||
only some of the conditions listed, they will pass, but if they have at least
|
||||
one more that isn't in the list, the request will be denied.
|
||||
"""
|
||||
directive @auth(roles: [USER_ROLE!], userIDField: String) on FIELD_DEFINITION
|
||||
directive @auth(
|
||||
roles: [USER_ROLE!]
|
||||
userIDField: String
|
||||
permit: [USER_AUTH_CONDITIONS!]
|
||||
) on FIELD_DEFINITION
|
||||
|
||||
################################################################################
|
||||
## Custom Scalar Types
|
||||
@@ -413,11 +439,6 @@ OIDCAuthIntegration provides a way to store Open ID Connect credentials. This
|
||||
will be used in the admin to provide staff logins for users.
|
||||
"""
|
||||
type OIDCAuthIntegration {
|
||||
"""
|
||||
id is the identifier of the OIDCAuthIntegration.
|
||||
"""
|
||||
id: ID!
|
||||
|
||||
"""
|
||||
enabled, when true, allows the integration to be enabled.
|
||||
"""
|
||||
@@ -444,53 +465,60 @@ type OIDCAuthIntegration {
|
||||
name: String
|
||||
|
||||
"""
|
||||
callbackURL is the URL that the user should be redirected to in order to start
|
||||
an authentication flow with the given integration. This field is not stored,
|
||||
callbackURL is the URL that the user should be redirected to in order to continue
|
||||
the authentication flow with the given integration. This field is not stored,
|
||||
and is instead computed from the Tenant.
|
||||
"""
|
||||
callbackURL: String!
|
||||
|
||||
"""
|
||||
redirectURL is the URL that the user should be redirected to in order to start
|
||||
an authentication flow with the given integration. This field is not stored,
|
||||
and is instead computed from the Tenant.
|
||||
"""
|
||||
redirectURL: String
|
||||
|
||||
"""
|
||||
clientID is the Client Identifier as defined in:
|
||||
|
||||
https://tools.ietf.org/html/rfc6749#section-2.2
|
||||
"""
|
||||
clientID: String! @auth(roles: [ADMIN])
|
||||
clientID: String @auth(roles: [ADMIN])
|
||||
|
||||
"""
|
||||
clientSecret is the Client Secret as defined in:
|
||||
|
||||
https://tools.ietf.org/html/rfc6749#section-2.3.1
|
||||
"""
|
||||
clientSecret: String! @auth(roles: [ADMIN])
|
||||
clientSecret: String @auth(roles: [ADMIN])
|
||||
|
||||
"""
|
||||
authorizationURL is defined as the `authorization_endpoint` in:
|
||||
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
"""
|
||||
authorizationURL: String! @auth(roles: [ADMIN])
|
||||
authorizationURL: String @auth(roles: [ADMIN])
|
||||
|
||||
"""
|
||||
tokenURL is defined as the `token_endpoint` in:
|
||||
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
"""
|
||||
tokenURL: String! @auth(roles: [ADMIN])
|
||||
tokenURL: String @auth(roles: [ADMIN])
|
||||
|
||||
"""
|
||||
jwksURI is defined as the `jwks_uri` in:
|
||||
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
"""
|
||||
jwksURI: String! @auth(roles: [ADMIN])
|
||||
jwksURI: String @auth(roles: [ADMIN])
|
||||
|
||||
"""
|
||||
issuer is defined as the `issuer` in:
|
||||
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
"""
|
||||
issuer: String! @auth(roles: [ADMIN])
|
||||
issuer: String @auth(roles: [ADMIN])
|
||||
}
|
||||
|
||||
##########################
|
||||
@@ -517,11 +545,18 @@ type GoogleAuthIntegration {
|
||||
clientSecret: String @auth(roles: [ADMIN])
|
||||
|
||||
"""
|
||||
callbackURL is the URL that the user should be redirected to in order to start
|
||||
callbackURL is the URL that the user should be redirected to in order to continue
|
||||
the authentication flow. This field is not stored, and is instead computed from
|
||||
the Tenant.
|
||||
"""
|
||||
callbackURL: String!
|
||||
|
||||
"""
|
||||
redirectURL is the URL that the user should be redirected to in order to start
|
||||
an authentication flow with the given integration. This field is not stored,
|
||||
and is instead computed from the Tenant.
|
||||
"""
|
||||
redirectURL: String!
|
||||
}
|
||||
|
||||
##########################
|
||||
@@ -548,11 +583,18 @@ type FacebookAuthIntegration {
|
||||
clientSecret: String @auth(roles: [ADMIN])
|
||||
|
||||
"""
|
||||
callbackURL is the URL that the user should be redirected to in order to start
|
||||
callbackURL is the URL that the user should be redirected to in order to continue
|
||||
the authentication flow. This field is not stored, and is instead computed from
|
||||
the Tenant.
|
||||
"""
|
||||
callbackURL: String!
|
||||
|
||||
"""
|
||||
redirectURL is the URL that the user should be redirected to in order to start
|
||||
an authentication flow with the given integration. This field is not stored,
|
||||
and is instead computed from the Tenant.
|
||||
"""
|
||||
redirectURL: String!
|
||||
}
|
||||
|
||||
##########################
|
||||
@@ -562,7 +604,7 @@ type FacebookAuthIntegration {
|
||||
type AuthIntegrations {
|
||||
local: LocalAuthIntegration!
|
||||
sso: SSOAuthIntegration!
|
||||
oidc: [OIDCAuthIntegration!]!
|
||||
oidc: OIDCAuthIntegration!
|
||||
google: GoogleAuthIntegration!
|
||||
facebook: FacebookAuthIntegration!
|
||||
}
|
||||
@@ -990,7 +1032,20 @@ type SSOProfile {
|
||||
id: String!
|
||||
}
|
||||
|
||||
union Profile = LocalProfile | OIDCProfile | SSOProfile
|
||||
type FacebookProfile {
|
||||
id: String!
|
||||
}
|
||||
|
||||
type GoogleProfile {
|
||||
id: String!
|
||||
}
|
||||
|
||||
union Profile =
|
||||
LocalProfile
|
||||
| OIDCProfile
|
||||
| SSOProfile
|
||||
| FacebookProfile
|
||||
| GoogleProfile
|
||||
|
||||
"""
|
||||
User is someone that leaves Comments, and logs in.
|
||||
@@ -1011,10 +1066,25 @@ type User {
|
||||
"""
|
||||
displayName: String
|
||||
|
||||
"""
|
||||
email is the current email address for the User.
|
||||
"""
|
||||
email: String
|
||||
@auth(
|
||||
roles: [ADMIN, MODERATOR]
|
||||
userIDField: "id"
|
||||
permit: [MISSING_NAME, MISSING_EMAIL]
|
||||
)
|
||||
|
||||
"""
|
||||
profiles is the array of profiles assigned to the user.
|
||||
"""
|
||||
profiles: [Profile!] @auth(roles: [ADMIN, MODERATOR], userIDField: "id")
|
||||
profiles: [Profile!]!
|
||||
@auth(
|
||||
roles: [ADMIN, MODERATOR]
|
||||
userIDField: "id"
|
||||
permit: [MISSING_NAME, MISSING_EMAIL]
|
||||
)
|
||||
|
||||
"""
|
||||
role is the current role of the User.
|
||||
@@ -1747,6 +1817,75 @@ type EditCommentPayload {
|
||||
## updateSettings
|
||||
##################
|
||||
|
||||
input SettingsOIDCAuthIntegrationInput {
|
||||
"""
|
||||
enabled, when true, allows the integration to be enabled.
|
||||
"""
|
||||
enabled: Boolean
|
||||
|
||||
"""
|
||||
allowRegistration when true will allow users that have not signed up
|
||||
before with this authentication integration to sign up.
|
||||
"""
|
||||
allowRegistration: Boolean
|
||||
|
||||
"""
|
||||
targetFilter will restrict where the authentication integration should be
|
||||
displayed. If the value of targetFilter is null, then the authentication
|
||||
integration should be displayed in all targets.
|
||||
"""
|
||||
targetFilter: SettingsAuthenticationTargetFilterInput
|
||||
|
||||
"""
|
||||
name is the label assigned to reference the provider of the OIDC integration,
|
||||
and will be used in situations where the name of the provider needs to be
|
||||
displayed, like the login button.
|
||||
"""
|
||||
name: String
|
||||
|
||||
"""
|
||||
clientID is the Client Identifier as defined in:
|
||||
|
||||
https://tools.ietf.org/html/rfc6749#section-2.2
|
||||
"""
|
||||
clientID: String
|
||||
|
||||
"""
|
||||
clientSecret is the Client Secret as defined in:
|
||||
|
||||
https://tools.ietf.org/html/rfc6749#section-2.3.1
|
||||
"""
|
||||
clientSecret: String
|
||||
|
||||
"""
|
||||
authorizationURL is defined as the `authorization_endpoint` in:
|
||||
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
"""
|
||||
authorizationURL: String
|
||||
|
||||
"""
|
||||
tokenURL is defined as the `token_endpoint` in:
|
||||
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
"""
|
||||
tokenURL: String
|
||||
|
||||
"""
|
||||
jwksURI is defined as the `jwks_uri` in:
|
||||
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
"""
|
||||
jwksURI: String
|
||||
|
||||
"""
|
||||
issuer is defined as the `issuer` in:
|
||||
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
"""
|
||||
issuer: String
|
||||
}
|
||||
|
||||
input SettingsEmailInput {
|
||||
"""
|
||||
enabled when True, will enable the emailing functionality in Talk.
|
||||
@@ -1858,6 +1997,7 @@ input SettingsFacebookAuthIntegrationInput {
|
||||
input SettingsAuthIntegrationsInput {
|
||||
local: SettingsLocalAuthIntegrationInput
|
||||
sso: SettingsSSOAuthIntegrationInput
|
||||
oidc: SettingsOIDCAuthIntegrationInput
|
||||
google: SettingsGoogleAuthIntegrationInput
|
||||
facebook: SettingsFacebookAuthIntegrationInput
|
||||
}
|
||||
@@ -2365,250 +2505,6 @@ type RegenerateSSOKeyPayload {
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
##################
|
||||
# createOIDCAuthIntegration
|
||||
##################
|
||||
|
||||
input CreateOIDCAuthIntegrationConfigurationInput {
|
||||
"""
|
||||
enabled, when true, allows the integration to be enabled.
|
||||
"""
|
||||
enabled: Boolean
|
||||
|
||||
"""
|
||||
allowRegistration when true will allow users that have not signed up
|
||||
before with this authentication integration to sign up.
|
||||
"""
|
||||
allowRegistration: Boolean
|
||||
|
||||
"""
|
||||
targetFilter will restrict where the authentication integration should be
|
||||
displayed. If the value of targetFilter is null, then the authentication
|
||||
integration should be displayed in all targets.
|
||||
"""
|
||||
targetFilter: SettingsAuthenticationTargetFilterInput
|
||||
|
||||
"""
|
||||
name is the label assigned to reference the provider of the OIDC integration,
|
||||
and will be used in situations where the name of the provider needs to be
|
||||
displayed, like the login button.
|
||||
"""
|
||||
name: String!
|
||||
|
||||
"""
|
||||
clientID is the Client Identifier as defined in:
|
||||
|
||||
https://tools.ietf.org/html/rfc6749#section-2.2
|
||||
"""
|
||||
clientID: String!
|
||||
|
||||
"""
|
||||
clientSecret is the Client Secret as defined in:
|
||||
|
||||
https://tools.ietf.org/html/rfc6749#section-2.3.1
|
||||
"""
|
||||
clientSecret: String!
|
||||
|
||||
"""
|
||||
authorizationURL is defined as the `authorization_endpoint` in:
|
||||
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
"""
|
||||
authorizationURL: String!
|
||||
|
||||
"""
|
||||
tokenURL is defined as the `token_endpoint` in:
|
||||
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
"""
|
||||
tokenURL: String!
|
||||
|
||||
"""
|
||||
jwksURI is defined as the `jwks_uri` in:
|
||||
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
"""
|
||||
jwksURI: String!
|
||||
|
||||
"""
|
||||
issuer is defined as the `issuer` in:
|
||||
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
"""
|
||||
issuer: String!
|
||||
}
|
||||
|
||||
input CreateOIDCAuthIntegrationInput {
|
||||
"""
|
||||
configuration contains the configuration to be used to create the auth integration.
|
||||
"""
|
||||
configuration: CreateOIDCAuthIntegrationConfigurationInput!
|
||||
|
||||
"""
|
||||
clientMutationId is required for Relay support.
|
||||
"""
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
type CreateOIDCAuthIntegrationPayload {
|
||||
"""
|
||||
integration is the OIDCAuthIntegration we just created.
|
||||
"""
|
||||
integration: OIDCAuthIntegration
|
||||
|
||||
"""
|
||||
settings is the Settings that the OIDCAuthIntegration was created on.
|
||||
"""
|
||||
settings: Settings
|
||||
|
||||
"""
|
||||
clientMutationId is required for Relay support.
|
||||
"""
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
##################
|
||||
# updateOIDCAuthIntegration
|
||||
##################
|
||||
|
||||
input UpdateOIDCAuthIntegrationConfigurationInput {
|
||||
"""
|
||||
enabled, when true, allows the integration to be enabled.
|
||||
"""
|
||||
enabled: Boolean
|
||||
|
||||
"""
|
||||
allowRegistration when true will allow users that have not signed up
|
||||
before with this authentication integration to sign up.
|
||||
"""
|
||||
allowRegistration: Boolean
|
||||
|
||||
"""
|
||||
targetFilter will restrict where the authentication integration should be
|
||||
displayed. If the value of targetFilter is null, then the authentication
|
||||
integration should be displayed in all targets.
|
||||
"""
|
||||
targetFilter: SettingsAuthenticationTargetFilterInput
|
||||
|
||||
"""
|
||||
name is the label assigned to reference the provider of the OIDC integration,
|
||||
and will be used in situations where the name of the provider needs to be
|
||||
displayed, like the login button.
|
||||
"""
|
||||
name: String
|
||||
|
||||
"""
|
||||
clientID is the Client Identifier as defined in:
|
||||
|
||||
https://tools.ietf.org/html/rfc6749#section-2.2
|
||||
"""
|
||||
clientID: String
|
||||
|
||||
"""
|
||||
clientSecret is the Client Secret as defined in:
|
||||
|
||||
https://tools.ietf.org/html/rfc6749#section-2.3.1
|
||||
"""
|
||||
clientSecret: String
|
||||
|
||||
"""
|
||||
authorizationURL is defined as the `authorization_endpoint` in:
|
||||
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
"""
|
||||
authorizationURL: String
|
||||
|
||||
"""
|
||||
tokenURL is defined as the `token_endpoint` in:
|
||||
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
"""
|
||||
tokenURL: String
|
||||
|
||||
"""
|
||||
jwksURI is defined as the `jwks_uri` in:
|
||||
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
"""
|
||||
jwksURI: String
|
||||
|
||||
"""
|
||||
issuer is defined as the `issuer` in:
|
||||
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
"""
|
||||
issuer: String
|
||||
}
|
||||
|
||||
input UpdateOIDCAuthIntegrationInput {
|
||||
"""
|
||||
id is the ID of the specific OpenID Connect integration that we are updating.
|
||||
"""
|
||||
id: ID!
|
||||
|
||||
"""
|
||||
configuration contains the configuration to be used to update the OpenID
|
||||
Connect integration.
|
||||
"""
|
||||
configuration: UpdateOIDCAuthIntegrationConfigurationInput!
|
||||
|
||||
"""
|
||||
clientMutationId is required for Relay support.
|
||||
"""
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
type UpdateOIDCAuthIntegrationPayload {
|
||||
"""
|
||||
integration is the OIDCAuthIntegration we just updated.
|
||||
"""
|
||||
integration: OIDCAuthIntegration
|
||||
|
||||
"""
|
||||
settings is the Settings that the OIDCAuthIntegration was updated on.
|
||||
"""
|
||||
settings: Settings
|
||||
|
||||
"""
|
||||
clientMutationId is required for Relay support.
|
||||
"""
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
##################
|
||||
# removeOIDCAuthIntegration
|
||||
##################
|
||||
|
||||
input RemoveOIDCAuthIntegrationInput {
|
||||
"""
|
||||
id is the ID of the specific OpenID Connect integration that we are deleting.
|
||||
"""
|
||||
id: ID!
|
||||
|
||||
"""
|
||||
clientMutationId is required for Relay support.
|
||||
"""
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
type RemoveOIDCAuthIntegrationPayload {
|
||||
"""
|
||||
integration is the OIDCAuthIntegration we just removed.
|
||||
"""
|
||||
integration: OIDCAuthIntegration
|
||||
|
||||
"""
|
||||
settings is the Settings that the OIDCAuthIntegration was removed on with the
|
||||
OIDCAuthIntegration removed from it.
|
||||
"""
|
||||
settings: Settings
|
||||
|
||||
"""
|
||||
clientMutationId is required for Relay support.
|
||||
"""
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
##################
|
||||
## createStory
|
||||
##################
|
||||
@@ -2935,6 +2831,118 @@ type RejectCommentPayload {
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
##################
|
||||
# setUsername
|
||||
##################
|
||||
|
||||
input SetUsernameInput {
|
||||
"""
|
||||
username is the desired username that should be set to the current User.
|
||||
"""
|
||||
username: String!
|
||||
|
||||
"""
|
||||
clientMutationId is required for Relay support.
|
||||
"""
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
type SetUsernamePayload {
|
||||
"""
|
||||
user is the possibly modified User.
|
||||
"""
|
||||
user: User!
|
||||
|
||||
"""
|
||||
clientMutationId is required for Relay support.
|
||||
"""
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
##################
|
||||
# setEmail
|
||||
##################
|
||||
|
||||
input SetEmailInput {
|
||||
"""
|
||||
email is the email address to be associated with the User.
|
||||
"""
|
||||
email: String!
|
||||
|
||||
"""
|
||||
clientMutationId is required for Relay support.
|
||||
"""
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
type SetEmailPayload {
|
||||
"""
|
||||
user is the possibly modified User.
|
||||
"""
|
||||
user: User!
|
||||
|
||||
"""
|
||||
clientMutationId is required for Relay support.
|
||||
"""
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
##################
|
||||
# setPassword
|
||||
##################
|
||||
|
||||
input SetPasswordInput {
|
||||
"""
|
||||
password is the password that should be associated with the User.
|
||||
"""
|
||||
password: String!
|
||||
|
||||
"""
|
||||
clientMutationId is required for Relay support.
|
||||
"""
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
type SetPasswordPayload {
|
||||
"""
|
||||
user is the possibly modified User.
|
||||
"""
|
||||
user: User!
|
||||
|
||||
"""
|
||||
clientMutationId is required for Relay support.
|
||||
"""
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
##################
|
||||
# updatePassword
|
||||
##################
|
||||
|
||||
input UpdatePasswordInput {
|
||||
"""
|
||||
password is the new password that should be associated with the User.
|
||||
"""
|
||||
password: String!
|
||||
|
||||
"""
|
||||
clientMutationId is required for Relay support.
|
||||
"""
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
type UpdatePasswordPayload {
|
||||
"""
|
||||
user is the possibly modified User.
|
||||
"""
|
||||
user: User!
|
||||
|
||||
"""
|
||||
clientMutationId is required for Relay support.
|
||||
"""
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
##################
|
||||
## Mutation
|
||||
##################
|
||||
@@ -2943,7 +2951,7 @@ type Mutation {
|
||||
"""
|
||||
createComment will create a Comment as the current logged in User.
|
||||
"""
|
||||
createComment(input: CreateCommentInput!): CreateCommentPayload
|
||||
createComment(input: CreateCommentInput!): CreateCommentPayload @auth
|
||||
|
||||
"""
|
||||
createCommentReply will create a Comment as the current logged in User that is
|
||||
@@ -2970,28 +2978,6 @@ type Mutation {
|
||||
regenerateSSOKey(input: RegenerateSSOKeyInput!): RegenerateSSOKeyPayload
|
||||
@auth(roles: [ADMIN])
|
||||
|
||||
"""
|
||||
createOIDCAuthIntegration will create a OpenID Connect auth integration.
|
||||
"""
|
||||
createOIDCAuthIntegration(
|
||||
input: CreateOIDCAuthIntegrationInput!
|
||||
): CreateOIDCAuthIntegrationPayload @auth(roles: [ADMIN])
|
||||
|
||||
"""
|
||||
updateOIDCAuthIntegration will update a given OpenID Connect auth integration.
|
||||
"""
|
||||
updateOIDCAuthIntegration(
|
||||
input: UpdateOIDCAuthIntegrationInput!
|
||||
): UpdateOIDCAuthIntegrationPayload @auth(roles: [ADMIN])
|
||||
|
||||
"""
|
||||
removeOIDCAuthIntegration will remove the specified OpenID Connect auth
|
||||
integration.
|
||||
"""
|
||||
removeOIDCAuthIntegration(
|
||||
input: RemoveOIDCAuthIntegrationInput!
|
||||
): RemoveOIDCAuthIntegrationPayload @auth(roles: [ADMIN])
|
||||
|
||||
"""
|
||||
createCommentReaction will create a Reaction authored by the current logged in
|
||||
User on a Comment.
|
||||
@@ -3073,6 +3059,32 @@ type Mutation {
|
||||
"""
|
||||
rejectComment(input: RejectCommentInput!): RejectCommentPayload
|
||||
@auth(roles: [MODERATOR, ADMIN])
|
||||
|
||||
"""
|
||||
setUsername will set the username on the current User if they have not set one
|
||||
before. This mutation will fail if the username is already set.
|
||||
"""
|
||||
setUsername(input: SetUsernameInput!): SetUsernamePayload
|
||||
@auth(permit: [MISSING_NAME, MISSING_EMAIL])
|
||||
|
||||
"""
|
||||
setEmail will set the email address on the current User if they have not set
|
||||
one already. This mutation will fail if the email address is already set.
|
||||
"""
|
||||
setEmail(input: SetEmailInput!): SetEmailPayload
|
||||
@auth(permit: [MISSING_EMAIL])
|
||||
|
||||
"""
|
||||
setPassword will set the password on the current User if they have not set
|
||||
one already. This mutation will fail if the password is already set.
|
||||
"""
|
||||
setPassword(input: SetPasswordInput!): SetPasswordPayload @auth
|
||||
|
||||
"""
|
||||
updatePassword allows the current logged in User to change their password if
|
||||
they already have one associated with them.
|
||||
"""
|
||||
updatePassword(input: UpdatePasswordInput!): UpdatePasswordPayload @auth
|
||||
}
|
||||
|
||||
################################################################################
|
||||
|
||||
Reference in New Issue
Block a user