Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Draft] Template for Instant User Management and SSO for X (web framework) #100

Merged
merged 5 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions blog/2023-08-02-secure-reactjs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
slug: instant-user-managemenet-and-sso-for-reactjs
title: Instant User Management, SSO, and Secure Pages for ReactJS
author: Phase Two
tags: [phase_two, tutorial, frameworks, reactjs]
---

import PhaseTwoStarterInstructions from "/templates/blog/_phase_two_starter_instructions.mdx";
import OIDC from "/templates/blog/_oidc_client_creation.mdx";
import NonAdminUser from "/templates/blog/_add_nonadmin_user.mdx";

In this article we'll be using [Keycloak](https://www.keycloak.org/) to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

:::info

If you just want to skip to the code, visit the Phase Two [ReactJS example](https://github.com/p2-inc/examples/tree/main/frameworks/reactjs).

:::

## Setting up a Keycloak Instance

<PhaseTwoStarterInstructions />

## Setting up an OIDC Client

<OIDC />

## Adding a Non-Admin User

<NonAdminUser />

## Setting up a ReactJS Project

:::info
We will use the Phase Two ReactJS example code here, but the logic could easily be applied to any existing application.
:::

1. Clone the Phase Two [example repo](https://github.com/p2-inc/examples/).
1. Open the ReactJS [folder](https://github.com/p2-inc/examples/tree/main/frameworks/reactjs) within `/frameworks/reactjs`.
1. Run `npm install` and then `npm start`. This example leverages [react-oidc-context](https://github.com/authts/react-oidc-context/tree/f175dcba6ab09871b027d6a2f2224a17712b67c5) (which uses [oidc-client-ts](https://github.com/authts/oidc-client-ts)) to provide hook and HOC support.
1. Open the `index.tsx` file. We will be updating a few values from the prior section where we set up our OIDC client. Taking the values from the OIDC Client Config section, set those values in the code.

```js
const authServerUrl = "https://euc1.auth.ac/auth/";
const realm = "shared-deployment-001";
const client = "reg-example-1";
```

Those are used to popluate the OIDC config

```js
const oidcConfig = {
authority: `${authServerUrl}realms/${realm}`,
client_id: client,
redirect_uri: "http://localhost:3000/authenticated",
onSigninCallback: (args: any) =>
window.history.replaceState(
{},
document.title,
window.location.pathname
),
};
```

The config is then provided to the `AuthProvider`.

```js
<AuthProvider {...oidcConfig}>
<App />
</AuthProvider>
```

At this point our entire applicationw will be able to access all information and methods needed to perform authentication. View `Auth.tsx` for exactly how the code is authenticating your user. The sections rendering the "Log in" and "Log out" buttons are conditional areas based on the authenticated context.

This login of using the hook to conditionally determine the Authenticated state, can be used to secure routes, components, and more.

1. Open [localhost:3000](http://localhost:3000). You will see the Phase Two example landing page. You current state should be "Not authenticated". Click **Log In**. This will redirect you to your login page.

:::info
Use the non-admin user created in the previous section to sign in.
:::

1. Enter the credentials of the non-admin user you created. Click **Submit**. You will then be redirected to the application. The Phase Two example landing page now loads your "Authenticated" state, displaying your user's email and their Token.
1. After your first log in, click **Log out**. Then click **Log in** again. Notice how this time you will not be redirected to sign in as your state is already in the browser. Neat! If you clear the browser state for that tab, then you will have to be redirected away to sign-in again.

## Learning more

Phase Two's enhanced Keycloak provides a many ways to quickly control and tweak the log in and user management experience. Our [blog](https://phasetwo.io/blog) has many use case from [customizing login pages](https://phasetwo.io/blog/customizing-login-pages), setting up [magic links](https://phasetwo.io/blog/set-up-magic-links) (password-less sign in), and [Organization](https://phasetwo.io/product/organizations) workflows.
57 changes: 24 additions & 33 deletions src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@
--openapi-code-dim-light: #aaaaa1 !important;

--color-wh: #fff;
--color-bg: #F4F4FC;
--color-main: #0D0F19;
--color-n1: #C2CAD0;
--color-n5: #3C474E;
--color-planbadge: #F36529;
--color-bg: #f4f4fc;
--color-main: #0d0f19;
--color-n1: #c2cad0;
--color-n5: #3c474e;
--color-planbadge: #f36529;
--soft-shadow-branded: 0px 2px 12px rgba(248, 204, 241, 0.55);
--soft-shadow-branded-strong: 0px 5px 12px rgba(248, 204, 241, 0.85);

Expand Down Expand Up @@ -75,10 +75,6 @@ body {
height: auto;
z-index: -1;
}
.page-identity {
background-image: url(/static/img/gradient-short-bg.png);
}

@media (min-width: 768px) {
.page-home {
left: 50%;
Expand All @@ -90,14 +86,17 @@ body {
}
}

.page-identity {
background-image: url(/static/img/gradient-short-bg.png);
}

a {
transition: .2s all;
transition: 0.2s all;
}
a:hover {
text-decoration: none;
}


.pt0 {
padding-top: 0 !important;
}
Expand All @@ -109,8 +108,8 @@ a:hover {
background-color: var(--ifm-color-primary);
color: var(--color-wh);
font-size: 16px;
font-weight: 550;;
transition: .2s all;
font-weight: 550;
transition: 0.2s all;
padding: 18px 24px;
box-shadow: 0px 16px 30px rgba(0, 0, 0, 0.2);
}
Expand All @@ -127,8 +126,8 @@ a:hover {
background-color: var(--ifm-color-primary);
color: var(--color-wh);
font-size: 13px;
font-weight: 700;;
transition: .2s all;
font-weight: 700;
transition: 0.2s all;
padding: 12px 16px;
}
.btnPrimary:hover {
Expand All @@ -144,8 +143,8 @@ a:hover {
color: var(--ifm-color-primary);
box-shadow: inset 0 0 0 1px var(--ifm-color-primary);
font-size: 13px;
font-weight: 700;;
transition: .2s all;
font-weight: 700;
transition: 0.2s all;
padding: 12px 16px;
}
.btnSecondary:hover {
Expand All @@ -163,13 +162,12 @@ a:hover {
line-height: 1.2em;
}
.btnReadMore img {
transition: .25s ease-out;
transition: 0.25s ease-out;
}
.btnReadMore:hover img {
transform: translateX(4px);
}


.codeBox {
background-color: var(--color-main);
border-radius: 16px;
Expand All @@ -178,7 +176,6 @@ a:hover {
min-height: 390px;
}


.bgImg {
position: absolute;
z-index: -1;
Expand Down Expand Up @@ -273,7 +270,6 @@ a:hover {
}
}


.docusaurus-highlight-code-line {
background-color: rgb(72, 77, 91);
display: block;
Expand Down Expand Up @@ -304,8 +300,8 @@ a:hover {
color: var(--ifm-color-primary);
box-shadow: inset 0 0 0 1px var(--ifm-color-primary);
font-size: 13px;
font-weight: 700;;
transition: .2s all;
font-weight: 700;
transition: 0.2s all;
padding: 12px 16px;
}
.navbar__items--right [buttontype="btnSecondary"]:hover {
Expand All @@ -321,8 +317,8 @@ a:hover {
background-color: var(--ifm-color-primary);
color: var(--color-wh);
font-size: 13px;
font-weight: 700;;
transition: .2s all;
font-weight: 700;
transition: 0.2s all;
padding: 12px 16px;
}
.navbar__items--right [buttontype="btnPrimary"]:hover {
Expand Down Expand Up @@ -362,7 +358,8 @@ a:hover {
background-color: var(--color-bg);
}
.dropdown > .navbar__link:after {
background: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNiIgaGVpZ2h0PSI0IiB2aWV3Qm94PSIwIDAgNiA0IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNMC41IDAuNzVMMyAzLjI1TDUuNSAwLjc1IiBzdHJva2U9IiMxRjJDMzQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8L3N2Zz4=") no-repeat center;
background: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNiIgaGVpZ2h0PSI0IiB2aWV3Qm94PSIwIDAgNiA0IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNMC41IDAuNzVMMyAzLjI1TDUuNSAwLjc1IiBzdHJva2U9IiMxRjJDMzQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8L3N2Zz4=")
no-repeat center;
width: 10px;
height: 8px;
border: none;
Expand Down Expand Up @@ -395,9 +392,7 @@ a:hover {
width: 100%;
}


@media (min-width: 768px) {

}
@media (min-width: 1200px) {
.pageHero {
Expand Down Expand Up @@ -432,7 +427,6 @@ a:hover {
height: auto;
}


.contentBlock {
margin: 80px auto;
position: relative;
Expand All @@ -455,7 +449,7 @@ a:hover {
margin: 0;
}
.contentBlockHead h2 + p {
margin-top: 24px;
margin-top: 24px;
}
.contentBlockBody {
max-width: 1120px;
Expand All @@ -473,7 +467,6 @@ a:hover {
}
}


.readMore {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -510,7 +503,6 @@ a:hover {
}
}


@media (max-width: 996px) {
.navbar .navbar__items {
flex: 1 1 auto;
Expand Down Expand Up @@ -624,7 +616,6 @@ a:hover {
color: #999;
}


.markdown img {
height: auto;
box-shadow: 0 0 2.5px;
Expand Down
Loading