Merge pull request #4293 from backstage/rugvip/autoauth
core: add single provider mode for SignInPage, with automatic sign-in
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core': patch
|
||||
---
|
||||
|
||||
Add a `prop` union for `SignInPage` that allows it to be used for just a single provider, with inline errors, and optionally with automatic sign-in.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core': patch
|
||||
---
|
||||
|
||||
Fix check that determines whether popup was closed or the messaging was misconfigured.
|
||||
@@ -130,6 +130,7 @@ middleware
|
||||
minikube
|
||||
Minikube
|
||||
misconfiguration
|
||||
misconfigured
|
||||
misgendering
|
||||
mkdocs
|
||||
Mkdocs
|
||||
|
||||
+2
-1
@@ -86,7 +86,8 @@ to a `guest` identity for all users, without any ID token. To enable sign-in, a
|
||||
`SignInPage` needs to be configured, which in turn has to supply a user to the
|
||||
app. The `@backstage/core` package provides a basic sign-in page that allows
|
||||
both the user and the app developer to choose between a couple of different
|
||||
sign-in methods.
|
||||
sign-in methods, or to designate a single provider that may also be logged in to
|
||||
automatically.
|
||||
|
||||
## Further Reading
|
||||
|
||||
|
||||
@@ -349,22 +349,18 @@ const app = createApp({
|
||||
apis,
|
||||
plugins: Object.values(plugins),
|
||||
components: {
|
||||
SignInPage: props => {
|
||||
return (
|
||||
<SignInPage
|
||||
{...props}
|
||||
providers={[
|
||||
{
|
||||
id: 'github-auth-provider',
|
||||
title: 'GitHub',
|
||||
message: 'Simple Backstage Application Login',
|
||||
apiRef: githubAuthApiRef,
|
||||
},
|
||||
]}
|
||||
align="center"
|
||||
/>
|
||||
);
|
||||
},
|
||||
SignInPage: props => (
|
||||
<SignInPage
|
||||
{...props}
|
||||
auto
|
||||
provider={{
|
||||
id: 'github-auth-provider',
|
||||
title: 'GitHub',
|
||||
message: 'Simple Backstage Application Login',
|
||||
apiRef: githubAuthApiRef,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
@@ -82,7 +82,9 @@ export function showLoginPopup(options: LoginPopupOptions): Promise<any> {
|
||||
let targetOrigin = '';
|
||||
|
||||
if (!popup || typeof popup.closed === 'undefined' || popup.closed) {
|
||||
reject(new Error('Failed to open auth popup.'));
|
||||
const error = new Error('Failed to open auth popup.');
|
||||
error.name = 'PopupRejectedError';
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -120,7 +122,7 @@ export function showLoginPopup(options: LoginPopupOptions): Promise<any> {
|
||||
const intervalId = setInterval(() => {
|
||||
if (popup.closed) {
|
||||
const errMessage = `Login failed, ${
|
||||
targetOrigin !== window.location.origin
|
||||
targetOrigin && targetOrigin !== window.location.origin
|
||||
? `Incorrect app origin, expected ${targetOrigin}`
|
||||
: 'popup was closed'
|
||||
}`;
|
||||
|
||||
@@ -14,30 +14,38 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Page } from '../Page';
|
||||
import { Header } from '../Header';
|
||||
import { Content } from '../Content/Content';
|
||||
import { ContentHeader } from '../ContentHeader/ContentHeader';
|
||||
import { Grid } from '@material-ui/core';
|
||||
import { Grid, Button, Typography } from '@material-ui/core';
|
||||
import { SignInPageProps, useApi, configApiRef } from '@backstage/core-api';
|
||||
import { useSignInProviders, getSignInProviders } from './providers';
|
||||
import { IdentityProviders } from './types';
|
||||
import { IdentityProviders, SignInConfig } from './types';
|
||||
import { Progress } from '../../components/Progress';
|
||||
import { useStyles } from './styles';
|
||||
import { GridItem, useStyles } from './styles';
|
||||
import { InfoCard } from '../InfoCard';
|
||||
|
||||
export type Props = SignInPageProps & {
|
||||
type MultiSignInPageProps = SignInPageProps & {
|
||||
providers: IdentityProviders;
|
||||
title?: string;
|
||||
align?: 'center' | 'left';
|
||||
};
|
||||
|
||||
export const SignInPage = ({
|
||||
type SingleSignInPageProps = SignInPageProps & {
|
||||
provider: SignInConfig;
|
||||
auto?: boolean;
|
||||
};
|
||||
|
||||
export type Props = MultiSignInPageProps | SingleSignInPageProps;
|
||||
|
||||
export const MultiSignInPage = ({
|
||||
onResult,
|
||||
providers = [],
|
||||
title,
|
||||
align = 'left',
|
||||
}: Props) => {
|
||||
}: MultiSignInPageProps) => {
|
||||
const configApi = useApi(configApiRef);
|
||||
const classes = useStyles();
|
||||
|
||||
@@ -69,3 +77,86 @@ export const SignInPage = ({
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export const SingleSignInPage = ({
|
||||
onResult,
|
||||
provider,
|
||||
auto,
|
||||
}: SingleSignInPageProps) => {
|
||||
const classes = useStyles();
|
||||
const authApi = useApi(provider.apiRef);
|
||||
const configApi = useApi(configApiRef);
|
||||
|
||||
const [retry, setRetry] = useState<{} | boolean | undefined>(auto);
|
||||
const [error, setError] = useState<Error>();
|
||||
|
||||
useEffect(() => {
|
||||
const login = async () => {
|
||||
const identity = await authApi.getBackstageIdentity({
|
||||
instantPopup: true,
|
||||
});
|
||||
|
||||
const profile = await authApi.getProfile();
|
||||
onResult({
|
||||
userId: identity!.id,
|
||||
profile: profile!,
|
||||
getIdToken: () => {
|
||||
return authApi.getBackstageIdentity().then(i => i!.idToken);
|
||||
},
|
||||
signOut: async () => {
|
||||
await authApi.signOut();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
if (retry) {
|
||||
login().catch(setError);
|
||||
}
|
||||
}, [onResult, authApi, retry]);
|
||||
|
||||
return (
|
||||
<Page themeId="home">
|
||||
<Header title={configApi.getString('app.title')} />
|
||||
<Content>
|
||||
<Grid
|
||||
container
|
||||
justify="center"
|
||||
spacing={2}
|
||||
component="ul"
|
||||
classes={classes}
|
||||
>
|
||||
<GridItem>
|
||||
<InfoCard
|
||||
variant="fullHeight"
|
||||
title={provider.title}
|
||||
actions={
|
||||
<Button
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
onClick={() => setRetry({})}
|
||||
>
|
||||
Sign In
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Typography variant="body1">{provider.message}</Typography>
|
||||
{error && error.name !== 'PopupRejectedError' && (
|
||||
<Typography variant="body1" color="error">
|
||||
{error.message}
|
||||
</Typography>
|
||||
)}
|
||||
</InfoCard>
|
||||
</GridItem>
|
||||
</Grid>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export const SignInPage = (props: Props) => {
|
||||
if ('provider' in props) {
|
||||
return <SingleSignInPage {...props} />;
|
||||
}
|
||||
|
||||
return <MultiSignInPage {...props} />;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user