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

[NEW] Add auth0 component #74

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
27 changes: 27 additions & 0 deletions app/.env.local.sample
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
##### BEGIN: Environment variables for firebase config. #####
# Environment variables for firebase-admin
GOOGLE_CREDS_PATH=required, path to your service account json file that you downloaded from firebase console.

Expand All @@ -19,3 +20,29 @@ NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=
# for cookies signing
COOKIE_SECRET_CURRENT=
COOKIE_SECRET_PREVIOUS=

##### END: Environment variables for firebase config. #####

#### BEGIN: Environment variables for auth0 config ####

# A long, secret value used to encrypt the session cookie
AUTH0_SECRET='a_long_random_string'
# The base url of your application
AUTH0_BASE_URL='http://localhost:3000'
# The url of your Auth0 tenant domain
AUTH0_ISSUER_BASE_URL='https://sample_app.us.auth0.com'
# Your Auth0 application's Client ID
AUTH0_CLIENT_ID='your client id'
# Your Auth0 application's Client Secret
AUTH0_CLIENT_SECRET='your client secret'
# API route for auth0 login (do not not change, since we have used custom routes for auth0)
NEXT_PUBLIC_AUTH0_LOGIN='/api/auth0/login'
# API route for auth0 profile (do not not change, since we have used custom routes for auth0)
NEXT_PUBLIC_AUTH0_PROFILE='/api/auth0/me'
# API route for auth0 callback (do not not change, since we have used custom routes for auth0)
AUTH0_CALLBACK='/api/auth0/callback'
# URL to redirect after logout (set a value to override default). Must be present in list of logout urls in auth0 dashboard.
AUTH0_POST_LOGOUT_REDIRECT='/'
# For more environment variables visit https://auth0.github.io/nextjs-auth0/modules/config.html

#### END: Environment variables for auth0 config ####
95 changes: 95 additions & 0 deletions app/components/auth/auth0/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
RC4Community uses `@auth0/nextjs-auth0` to integrate authentication with Auth0.

## Setting up
1. Set up environment variables
```
# A long, secret value used to encrypt the session cookie
AUTH0_SECRET='a_long_random_string'
# The base url of your application
AUTH0_BASE_URL='http://localhost:3000'
# The url of your Auth0 tenant domain
AUTH0_ISSUER_BASE_URL='https://sample_app.us.auth0.com'
# Your Auth0 application's Client ID
AUTH0_CLIENT_ID='your client id'
# Your Auth0 application's Client Secret
AUTH0_CLIENT_SECRET='your client secret'
# API route for auth0 login (do not not change, since we have used custom routes for auth0)
NEXT_PUBLIC_AUTH0_LOGIN='/api/auth0/login'
# API route for auth0 profile (do not not change, since we have used custom routes for auth0)
NEXT_PUBLIC_AUTH0_PROFILE='/api/auth0/me'
# API route for auth0 callback (do not not change, since we have used custom routes for auth0)
AUTH0_CALLBACK='/api/auth0/callback'
# URL to redirect after logout (set a value to override default). Must be present in list of logout urls in auth0 dashboard.
AUTH0_POST_LOGOUT_REDIRECT='/'
```
For more environment variables visit [https://auth0.github.io/nextjs-auth0/modules/config.html](https://auth0.github.io/nextjs-auth0/modules/config.html)

2. You must add callback url and logout urls accordingly in application settings in auth0 dashboard.
3. Wrap your app with `<UserProvider>` component.
```
import '/styles/globals.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import Layout from '../components/layout';
import SSRProvider from 'react-bootstrap/SSRProvider';
import { UserProvider } from '@auth0/nextjs-auth0';

function MyApp({ Component, pageProps }) {
return (
<SSRProvider>
<UserProvider>
<Layout menu={pageProps}>
<Component {...pageProps} />
</Layout>
</UserProvider>
</SSRProvider>
);
}

export default MyApp;
```
4. Get login url from `getAuth0LoginURL()` and logout url from `getAuth0LogoutURL()`. Use `useUser()` hook to get user details.
```
import { useUser, withPageAuthRequired } from '@auth0/nextjs-auth0';
import { getAuth0LoginURL, getAuth0LogoutURL } from '/app/components/auth/auth0';
export default () => {
const { user, error, isLoading } = useUser();
if (isLoading) return <div>Loading...</div>;
if (error) return <div>{error.message}</div>;
if (user) {
return (
<div>
Welcome {user.name}! <a href={getAuth0LogoutURL()}>Logout</a>
</div>
);
}
return <a href={getAuth0LoginURL({redirectToThisPage: true})}>Login</a>;
};
```

5. To protect a page and api route, use `withPageAuthRequired` and `withApiAuthRequired`
```
import { withPageAuthRequired } from '@auth0/nextjs-auth0';

....

export const getServerSideProps = withPageAuthRequired({
async getServerSideProps(){
return {
props: {
customProp: "customPropValue"
}
}
}
});

```
```
import { withApiAuthRequired, getSession } from '@auth0/nextjs-auth0';

export default withApiAuthRequired(function ProtectedRoute(req, res) {
const session = getSession(req, res);
...
});
```

To read more about using `@auth0/nextjs-auth0` visit https://github.com/auth0/nextjs-auth0#readme
9 changes: 9 additions & 0 deletions app/components/auth/auth0/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Auth0AuthMenuButtonModule from './ui/Auth0AuthMenuButton';
import Auth0UserInfoModule from './ui/Auth0UserInfo';
import functions from './lib/functions';

export const Auth0AuthMenuButton = Auth0AuthMenuButtonModule;
export const Auth0AuthUserInfo = Auth0UserInfoModule;

export const getAuth0LoginURL = functions.getAuth0LoginURL;
export const getAuth0LogoutURL = functions.getAuth0LogoutURL;
47 changes: 47 additions & 0 deletions app/components/auth/auth0/lib/functions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import urlJoin from 'url-join';
const prepareUrl = (url,returnTo=null,redirectToThisPage=false) => {
let returnUrl = urlJoin(process.env.AUTH0_BASE_URL || '/', url);
if(typeof window !== 'undefined'){
if(returnTo){
return returnUrl + '?returnTo='+encodeURIComponent(returnTo);
} else if(redirectToThisPage){
return returnUrl + '?returnTo='+encodeURIComponent(window.location.href);
}
}
return returnUrl;
}

/**
*
* @param {String} returnTo
* URL to redirect to after logout.
* @param {boolean} redirectToThisPage
* Whether to redirect to the url where the function is called.
* @returns logoutUrl
*/
export const getAuth0LoginURL = ({
returnTo = null,
redirectToThisPage = false
} = {}) => {
return prepareUrl('/api/auth0/login',returnTo,redirectToThisPage)
};

/**
*
* @param {String} returnTo
* URL to redirect to after logout. The url passed must be added in logout urls list auth0 dashboard.
* @param {boolean} redirectToThisPage
* Whether to redirect to the url where the function is called. The url must be added in logout urls list in auth0 dashboard.
* @returns logoutUrl
*/
export const getAuth0LogoutURL = ({
returnTo = null,
redirectToThisPage = false
} = {}) => {
return prepareUrl('/api/auth0/logout',returnTo,redirectToThisPage)
};

export default {
getAuth0LoginURL,
getAuth0LogoutURL
};
45 changes: 45 additions & 0 deletions app/components/auth/auth0/styles/Auth0AuthMenuButton.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
.authDialogWrapper {
position: relative;
}
.authContainer {
display: block;
position: absolute;
right: 8px;
top: 62px;
width: 354px;
max-height: -webkit-calc(100vh - 62px - 100px);
max-height: calc(100vh - 62px - 100px);
overflow-y: auto;
overflow-x: hidden;
border-radius: 8px;
margin-left: 12px;
z-index: 991;
line-height: normal;
background: #fff;
border: 1px solid #ccc;
border-color: rgba(0,0,0,.2);
color: #000;
-webkit-box-shadow: 0 2px 10px rgb(0 0 0 / 20%);
box-shadow: 0 2px 10px rgb(0 0 0 / 20%);
-webkit-user-select: text;
user-select: text;
}

.avatar {
background: var(--bs-gray-300);
border-radius: 50%;
width: 42px;
height: 42px;
display: flex;
justify-content: center;
align-items: center;
}

.avatarButton {
background: none;
border: none;
}

.avatarButton:focus {
outline: none;
}
46 changes: 46 additions & 0 deletions app/components/auth/auth0/ui/Auth0AuthMenuButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useState } from "react";
import { NoUserAvatar } from "../../NoUserAvatar";
import styles from "../styles/Auth0AuthMenuButton.module.css";
import { useUser } from "@auth0/nextjs-auth0";
import Auth0UserInfo from "./Auth0UserInfo";
import { getAuth0LoginURL } from "../lib/functions";

export default function Auth0AuthMenuButton({}){
const {user} = useUser();
const [isOpen,setOpen] = useState(false);
return (
<div className={styles.authDialogWrapper}>
<div className={styles.avatar}>
{user?
<button className={styles.avatarButton} onClick={() => setOpen(!isOpen)}>
<span className="d-flex align-items-center">
{
user?.picture ?
<img src={user.picture}
alt={user.name}
className="rounded-circle"
height="42px"
width="42px" />
:
<NoUserAvatar name={user.name} size="42" />
}
</span>
</button>
:
<a href={getAuth0LoginURL()}>
<button className={styles.avatarButton}>
<span className="d-flex align-items-center">
<NoUserAvatar size="42" />
</span>
</button>
</a>
}
</div>
{ user && isOpen &&
<div className={styles.authContainer}>
<Auth0UserInfo/>
</div>
}
</div>
)
}
43 changes: 43 additions & 0 deletions app/components/auth/auth0/ui/Auth0UserInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useUser } from "@auth0/nextjs-auth0";
import { Button } from "react-bootstrap";
import { NoUserAvatar } from "../../NoUserAvatar";
import { getAuth0LogoutURL } from "../lib/functions";

export default function Auth0UserInfo(){
const {user} = useUser();
if(!user)
return <div/>;
return (
<>
<div className="d-flex flex-column align-items-center mt-4 mb-3 ml-3 mr-3 border-bottom">
<div className="mb-1">
{
user.picture ?
<img src={user.picture}
alt={user.name}
style={{
borderRadius: "50%"
}}
height="64px"
width="64px" />
:
<NoUserAvatar size="64" name={user.name}/>
}
</div>
<div className="font-weight-bold mb-1">
{user.name}
</div>
<div
className="mb-1"
style={{color: "var(--bs-gray-700)"}}>
{user.email}
</div>
</div>
<div className="d-flex justify-content-center mb-4 mt-3 ml-3 mr-3">
<a href={getAuth0LogoutURL()}>
<Button variant="secondary">Sign Out</Button>
</a>
</div>
</>
)
}
3 changes: 1 addition & 2 deletions app/components/layout.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import '../styles/Layout.module.css'
import { withFirebaseAuthUser } from './auth/firebase';
import Footer from './footer'
import Menubar from './menubar'

Expand All @@ -15,4 +14,4 @@ function Layout(props) {
)
}

export default withFirebaseAuthUser()(Layout);
export default Layout;
4 changes: 2 additions & 2 deletions app/components/menubar.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState } from 'react';
import { Navbar, Nav, NavDropdown, Container } from 'react-bootstrap';
import styles from '../styles/Menubar.module.css';
import { FirebaseAuthMenuButton } from './auth/firebase';
import { Auth0AuthMenuButton } from './auth/auth0';
import BrandLogo from "./brandlogo";
import RocketChatLinkButton from './rocketchatlinkbutton';

Expand Down Expand Up @@ -71,7 +71,7 @@ export default function Menubar(props) {
</RocketChatLinkButton>
</Navbar.Collapse>
<div className="mx-1">
<FirebaseAuthMenuButton/>
<Auth0AuthMenuButton/>
</div>
</Navbar>
</Container>
Expand Down
4 changes: 3 additions & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"lint": "next lint"
},
"dependencies": {
"@auth0/nextjs-auth0": "^1.7.0",
"bootstrap": "^5.1.3",
"firebase": "^9.6.3",
"next": "12.0.7",
Expand All @@ -20,7 +21,8 @@
"react-i18next": "^11.15.3",
"react-icons": "^4.3.1",
"react-slick": "^0.28.1",
"slick-carousel": "^1.8.1"
"slick-carousel": "^1.8.1",
"url-join": "^4.0.1"
},
"devDependencies": {
"eslint": "8.6.0",
Expand Down
12 changes: 6 additions & 6 deletions app/pages/_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import '/styles/globals.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import Layout from '../components/layout';
import SSRProvider from 'react-bootstrap/SSRProvider';
import { initAuth } from '../components/auth/firebase';

initAuth();
import { UserProvider } from '@auth0/nextjs-auth0';

function MyApp({ Component, pageProps }) {
return (
<SSRProvider>
<Layout menu={pageProps}>
<Component {...pageProps} />
</Layout>
<UserProvider>
<Layout menu={pageProps}>
<Component {...pageProps} />
</Layout>
</UserProvider>
</SSRProvider>
);
}
Expand Down
Loading