[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:
Kiwi
2018-12-20 22:32:04 +01:00
committed by GitHub
parent 326a10dc5d
commit 065cb4b03a
331 changed files with 12476 additions and 7163 deletions
@@ -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);
};
}
+299 -287
View File
@@ -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
}
################################################################################