Skip to content

Commit 69f20ba

Browse files
Rafał Dzięgielewskigitbook-bot
Rafał Dzięgielewski
authored andcommitted
GITBOOK-110: Firebase Auth
1 parent 34b61f9 commit 69f20ba

File tree

10 files changed

+460
-46
lines changed

10 files changed

+460
-46
lines changed
Loading
Loading

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ AdminJS also generates it's own REST API which you can use outside of the admin
1515
![AdminJS Demo](.gitbook/assets/anim.gif)
1616

1717
{% hint style="info" %}
18-
Visit [our demo](https://adminjs-demo.herokuapp.com/admin/login) for a live preview.\
18+
Visit our [demo](https://demo.adminjs.co) for a live preview.\
1919
\
2020
 `Email: [email protected]`\
2121
 `Password: password`

SUMMARY.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
* [AdminJS](README.md)
44
* [Contribute](https://github.com/orgs/SoftwareBrothers/projects/5)
5-
* [Stats Dashboard](https://stats.adminjs.co)
5+
* [Demo](https://demo.adminjs.co)
6+
* [Addons Marketplace](https://cloud.adminjs.co)
67

78
## Installation
89

@@ -51,6 +52,12 @@
5152
* [Delete](basics/api/delete.md)
5253
* [Bulk Delete](basics/api/bulk-delete.md)
5354
* [Themes](basics/themes.md)
55+
* [Authentication](basics/authentication/README.md)
56+
* [FirebaseAuthProvider](basics/authentication/firebaseauthprovider.md)
57+
58+
***
59+
60+
* [How to write an addon?](how-to-write-an-addon.md)
5461

5562
## UI Customization
5663

basics/authentication/README.md

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Authentication
2+
3+
Version 7.4 of `adminjs` package introduces authentication providers which both simplify and extend authentication possibilities in your application.
4+
5+
Every authentication provider extends `BaseAuthProvider` class (exported by `adminjs` package). `adminjs` also exports `DefaultAuthProvider` which functions exactly the same as the current `authenticate` method.
6+
7+
### DefaultAuthProvider
8+
9+
As of version >=7.4.0 of `adminjs`, `DefaultAuthProvider` is an alternative to `authenticate` method. In the next major release, `authenticate` method will be removed in favour of auth providers.
10+
11+
<pre class="language-typescript"><code class="lang-typescript"><strong>import { DefaultAuthProvider } from 'adminjs';
12+
</strong><strong>
13+
</strong><strong>import componentLoader from '&#x3C;path to your component loader>';
14+
</strong><strong>
15+
</strong><strong>// Placeholder authentication function, add your logic for authenticating users
16+
</strong>const authenticate = ({ email, password }, ctx) => {
17+
return { email };
18+
}
19+
20+
const authProvider = new DefaultAuthProvider({
21+
componentLoader,
22+
authenticate,
23+
});
24+
25+
// ...
26+
27+
// Express example, in other plugins the change is exactly the same
28+
// "provider" should be configured at the same level as "authenticate" previously
29+
const router = buildAuthenticatedRouter(
30+
admin,
31+
{
32+
// "authenticate" was here
33+
cookiePassword: 'test',
34+
provider: authProvider,
35+
},
36+
null,
37+
{
38+
secret: 'test',
39+
resave: false,
40+
saveUninitialized: true,
41+
}
42+
);
43+
</code></pre>
44+
45+
By migrating to class syntax, you should be able to modify any existing auth provider without making additional changes to your framework's plugin.
46+
47+
### BaseAuthProvider
48+
49+
```typescript
50+
export interface LoginHandlerOptions {
51+
data: Record<string, any>;
52+
query?: Record<string, any>;
53+
params?: Record<string, any>;
54+
headers: Record<string, any>;
55+
}
56+
57+
export interface RefreshTokenHandlerOptions extends LoginHandlerOptions {}
58+
59+
export class BaseAuthProvider {
60+
public getUiProps(): Record<string, any> {
61+
return {}
62+
}
63+
64+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
65+
public async handleLogin(opts: LoginHandlerOptions, context?: any) {
66+
throw new NotImplementedError('BaseAuthProvider#handleLogin')
67+
}
68+
69+
public async handleLogout(context?: any): Promise<any> {
70+
return Promise.resolve()
71+
}
72+
73+
public async handleRefreshToken(opts: RefreshTokenHandlerOptions, context?: any): Promise<any> {
74+
return Promise.resolve({})
75+
}
76+
}
77+
```
78+
79+
You can use `getUiProps` method to define configuration that will be sent to the frontend.
80+
81+
`handleLogin` method is neccessary to sign in your user and should return a user object or `null`, for example:
82+
83+
```typescript
84+
override async handleLogin(opts: LoginHandlerOptions, context) {
85+
const { data = {} } = opts
86+
const { email, password } = data
87+
88+
return this.authenticate({ email, password }, context)
89+
}
90+
```
91+
92+
`context` of `handleLogin` will always be an object containing Request/Response objects specific to your framework of choice.
93+
94+
`handleLogout` and `handleRefreshToken` are optional. `handleLogout` will be called before your user's session is destroyed in case you have to perform additional actions to log out the user. `handleRefreshToken` can be used to refresh your user's session if it's matched with an external authentication service. `handleRefreshToken` should return an updated user object (i. e. with a new access token). It is not used by default, but you can override `AuthenticationBackgroundComponent` component to periodically refresh your session.
95+
96+
```typescript
97+
import { useCurrentAdmin } from 'adminjs';
98+
99+
const api = new ApiClient();
100+
101+
// ...
102+
103+
const AuthenticationBackgroundComponentOverride = () => {
104+
const [currentAdmin, setCurrentAdmin] = useCurrentAdmin();
105+
// ...
106+
// A part of your code responsible for refreshing user's session
107+
const requestBody = {};
108+
const response = await api.refreshToken(requestBody);
109+
110+
const { data } = response;
111+
112+
setCurrentAdmin(data);
113+
// ...
114+
return null;
115+
}
116+
117+
export default AuthenticationBackgroundComponentOverride;
118+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
---
2+
description: '@adminjs/firebase-auth'
3+
---
4+
5+
# FirebaseAuthProvider
6+
7+
`@adminjs/firebase-auth` is an authentication provider which allows you to sign in using Firebase Authentication.
8+
9+
10+
11+
<figure><img src="../../.gitbook/assets/Screenshot 2023-11-22 at 13.25.33.png" alt=""><figcaption></figcaption></figure>
12+
13+
## Prerequisites
14+
15+
Make sure you follow official Firebase documentation to properly set up Firebase Authentication in Firebase Console: [https://firebase.google.com/docs/auth](https://firebase.google.com/docs/auth)
16+
17+
## Installation
18+
19+
`@adminjs/firebase-auth` is a premium feature which can be purchased at [https://cloud.adminjs.co](https://cloud.adminjs.co)
20+
21+
All premium features currently use **One Time Payment** model and you can use them in all apps that belong to you. Once you purchase the addon, you will receive a license key which you should provide in `@adminjs/firebase-auth` configuration in your application's code.
22+
23+
Installing the library:
24+
25+
```bash
26+
$ yarn add @adminjs/firebase-auth
27+
```
28+
29+
The license key should be provided via `FirebaseAuthProvider` constructor:
30+
31+
```typescript
32+
new FirebaseAuthProvider({
33+
licenseKey: process.env.LICENSE_KEY,
34+
// the rest of the config
35+
})
36+
```
37+
38+
If you encounter any issues or require help installing the package please contact us through our Discord server.
39+
40+
## Usage
41+
42+
`FirebaseAuthProvider` requires you to prepare Firebase-specific configuration.
43+
44+
#### UI Config
45+
46+
`@adminjs/firebase-auth` uses `firebaseui-web` to generate Firebase UI inside the login form and it requires you to configure it. Please refer to the following link for configuration options: [https://github.com/firebase/firebaseui-web/blob/master/types/index.d.ts#L115](https://github.com/firebase/firebaseui-web/blob/master/types/index.d.ts#L115)
47+
48+
Note that you can only configure raw values but you will not be able to configure functions or callbacks this way. A workaround will be described in the later part of this documentation.
49+
50+
```typescript
51+
import { EmailAuthProvider } from 'firebase/auth';
52+
53+
const uiConfig = {
54+
popupMode: true,
55+
signInFlow: 'popup',
56+
signInOptions: [
57+
{
58+
provider: EmailAuthProvider.PROVIDER_ID,
59+
disableSignUp: {
60+
status: true,
61+
},
62+
},
63+
],
64+
};
65+
```
66+
67+
#### Firebase Configuration
68+
69+
`@adminjs/firebase-auth` initializes a Firebase App in the Login view. Make sure you copy the configuration from your project in Firebase Console.
70+
71+
```typescript
72+
const firebaseConfig = {
73+
apiKey: 'AIza...',
74+
authDomain: 'XXXX.firebaseapp.com',
75+
projectId: 'XXXX',
76+
storageBucket: 'XXXX.appspot.com',
77+
messagingSenderId: '11111111111',
78+
appId: '1:11111111111:web:abcdef',
79+
};
80+
```
81+
82+
You will most likely also have to initialize a Firebase app on your server's end to verify the user's access token later:
83+
84+
```typescript
85+
import { initializeApp } from 'firebase-admin/app';
86+
87+
// ...
88+
89+
const firebaseApp = initializeApp(firebaseConfig);
90+
```
91+
92+
#### Authenticate method
93+
94+
Lastly, you must define an `authenticate` method which you will use to verify the user's access token and return the user object.
95+
96+
```typescript
97+
import { getAuth } from 'firebase-admin/auth';
98+
import { FirebaseAuthenticatePayload } from '@adminjs/firebase-auth';
99+
100+
export const authenticate = async ({
101+
accessToken,
102+
}: FirebaseAuthenticatePayload) => {
103+
const auth = getAuth(firebaseApp);
104+
105+
try {
106+
const decodedToken = await auth.verifyIdToken(accessToken);
107+
108+
return {
109+
id: decodedToken.uid,
110+
email: decodedToken.email ?? '',
111+
avatarUrl: decodedToken.picture,
112+
};
113+
} catch (error) {
114+
console.log(error);
115+
return null;
116+
}
117+
};
118+
```
119+
120+
### FirebaseAuthProvider Configuration
121+
122+
After you are done with Firebase-specific configuration, you can instantiate `FirebaseAuthProvider`:
123+
124+
```typescript
125+
import { FirebaseAuthProvider } from '@adminjs/firebase-auth';
126+
import componentLoader from '<path to your component loader file>';
127+
128+
// ... assume Firebase related configuration is in the same file
129+
130+
const authProvider = new FirebaseAuthProvider({
131+
// make sure that the same ComponentLoader instance is configured in AdminJS!
132+
componentLoader,
133+
uiConfig,
134+
firebaseConfig,
135+
authenticate,
136+
licenseKey: process.env.LICENSE_KEY,
137+
});
138+
139+
// ...
140+
141+
// Add "provider" to authentication options of your framework plugin, Express example:
142+
143+
const router = buildAuthenticatedRouter(
144+
admin,
145+
{
146+
cookiePassword: 'test',
147+
provider: authProvider,
148+
},
149+
null,
150+
{
151+
secret: 'test',
152+
resave: false,
153+
saveUninitialized: true,
154+
}
155+
);
156+
```
157+
158+
With your plugin and auth provider configured you should be able to restart your server and a new login page with embedded Firebase UI should appear.
159+
160+
## Troubleshooting
161+
162+
#### How to prevent @adminjs/firebase-auth from overriding the Login page?
163+
164+
By default, `@adminjs/firebase-auth` will override your Login page with it's own UI. You can disable that by adding `overrideLogin: false` in `FirebaseAuthProvider` configuration:
165+
166+
```typescript
167+
const authProvider = new FirebaseAuthProvider({
168+
componentLoader,
169+
uiConfig,
170+
firebaseConfig,
171+
authenticate,
172+
licenseKey: process.env.LICENSE_KEY,
173+
overrideLogin: false,
174+
});
175+
```
176+
177+
If you do this, though, Firebase won't render it's own UI. You will have to create your own Login page and import `FirebaseAuthForm` component from `@adminjs/firebase-auth` and put it wherever you want in your own Login component.
178+
179+
```typescript
180+
import { FirebaseAuthForm } from '@adminjs/firebase-auth/components'
181+
182+
const CustomLogin = () => {
183+
return <FirebaseAuthForm />
184+
}
185+
186+
export default CustomLogin
187+
```
188+
189+
Remember to override `Login` using your component loader:
190+
191+
```typescript
192+
componentLoader.override('Login', '<path to custom login>');
193+
```
194+
195+
#### How to configure functions and callbacks of UI Config?
196+
197+
Follow the steps above to create your custom Login page. Create UI configuration in your custom component and provide it as props of `FirebaseAuthForm`:
198+
199+
```typescript
200+
const uiConfig = {/* custom config */};
201+
202+
const CustomLogin = () => {
203+
return <FirebaseAuthForm uiConfig={uiConfig} />
204+
}
205+
```
206+
207+
#### How to customize the look of Firebase UI?
208+
209+
If you decide to follow the steps above you will be able to create a styled component out of `FirebaseAuthForm` and customize it fully.
210+
211+
Alternatively, you can create a CSS file and add it to your app's assets.
212+
213+
```css
214+
.adminjs_firebaseui-container {
215+
background: red;
216+
}
217+
```
218+
219+
Make sure the CSS file is present in your server's public assets directory. Lastly, configure `assets` of AdminJS:
220+
221+
```typescript
222+
const admin = new AdminJS({
223+
assets: {
224+
styles: ['/firebase-ui.css'],
225+
},
226+
// other config
227+
})
228+
```

0 commit comments

Comments
 (0)