Skip to content

Commit

Permalink
feat(auth): Add support for OpenID Connect provider (#6574)
Browse files Browse the repository at this point in the history
  • Loading branch information
Babsvik committed Jan 27, 2023
1 parent 1788a2d commit 469bf00
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .spellcheck.dict.txt
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,12 @@ pre-release
pre-rendered
preflight
preloaded
prepended
programmatically
PRs
PubSub
qa
react-native-app-auth
react-native-firebase
react-native-mlkit
realtime
Expand Down
2 changes: 1 addition & 1 deletion docs/auth/multi-factor-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Multi-factor Auth
description: Increase security by adding Multi-factor authentication to your app.
next: /firestore/usage
previous: /auth/phone-auth
previous: /auth/oidc-auth
---

# iOS Setup
Expand Down
68 changes: 68 additions & 0 deletions docs/auth/oidc-auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
title: OpenID Connect Authentication
description: Sign-in users using OpenID Connect.
next: /auth/multi-factor-auth
previous: /auth/phone-auth
---

React Native Firebase provides supports integrating with OpenID Connect providers. The authentication with these
different providers is left to you to implement due to the various implementations and flows possible.

Here we will demonstrate a minimal example of how you could do this using the package [react-native-app-auth](https://github.com/FormidableLabs/react-native-app-auth) to authenticate with the provider. Then after we have authenticated with the provider, we use the ID Token from the provider to authenticate with `react-native-firebase`. But you have to handle the flow to get the ID token and you should do things like logging the user out from the provider when they logout or revoke the token. Again this all depends on the provider, your flow and your use-case.

# Getting started

To get started with OpenID Connect authentication you need to do the following:

1. Setup or get the configuration from the provider you want to use
2. Add the provider in the firebase console
3. Authenticate in the app using `react-native-app-auth` and `react-native-firebase`

## 1. Setup or get the configuration from the provider you want to use

As stated before, this will vary a lot from provider to provider and your use-case. You need to find and look the documentation for the provider you want to use and follow that documentation to setup a working provider.
You can see examples of "Tested OpenID providers" from [react-native-app-auth here](https://github.com/FormidableLabs/react-native-app-auth#tested-openid-providers) and how you do this will depend on what provider you want to use. But you need to complete the setup or configuration of the provider you want to use before you continue here.

## 2. Add the provider in the Firebase console

Doing the steps below will allow you to add the provider to the Firebase project.
If the provider is not added there, you won't be able to use the `signInWithCredential` method, since Firebase will not be able to use the credential if the provider does not exist in the project.

1. Firebase console in the project you want to add OpenID Connect to
2. Authentication
3. Sign-in method
4. If you have added "Sign-in providers" already, click "Add new provider"
5. Under "Custom providers" choose "OpenID Connect"
6. Toggle on the Enabled at the top to the right of "Open ID Connect"
7. Fill out the details like: "Name", "Client ID", "Issuer (URL)" and "Client secret". These values have to correspond to the OpenID Connect provider you want to use.
8. Note down the Provider ID below name, if you type in "azure_test" in the name field. Notice how it says below the field: "Provider ID: oidc.azure_test" so this value will be prepended with "oidc." We will use this later when authenticating the user.

## 3. Authenticate in the app using "react-native-app-auth" and "react-native-firebase"

Before you use `react-native-app-auth` you have to complete the setup in their [docs](https://github.com/FormidableLabs/react-native-app-auth#getting-started).

The example below demonstrates how you could setup such a flow within your own application:

```jsx
import auth from '@react-native-firebase/auth';
import { authorize } from 'react-native-app-auth';

// using react-native-app-auth to get oauth token from Azure AD
const config = {
issuer: 'https://login.microsoftonline.com/XXX/v2.0',
clientId: 'XXXX',
redirectUrl: 'msauth.your.bundle.id://auth/',
scopes: ['openid', 'profile', 'email', 'offline_access'],
useNonce: false,
};

// Log in to get an authentication token
const authState = await authorize(config);

const credential = auth.OIDCAuthProvider.credential(
'azure_test', // this is the "Provider ID" value from the firebase console
authState.idToken,
);

await auth().signInWithCredential(credential);
```
2 changes: 1 addition & 1 deletion docs/auth/phone-auth.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Phone Authentication
description: Sign-in users with their phone number.
next: /auth/multi-factor-auth
next: /auth/oidc-auth
previous: /auth/social-auth
---

Expand Down
2 changes: 2 additions & 0 deletions docs/sidebar.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
- '/auth/social-auth'
- - Phone Auth
- '/auth/phone-auth'
- - OpenID Connect Auth
- '/auth/oidc-auth'
- - Multi-factor Auth
- '/auth/multi-factor-auth'
- '//static.invertase.io/assets/firebase/authentication.svg'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1554,6 +1554,10 @@ private void reauthenticateWithCredential(
/** Returns an instance of AuthCredential for the specified provider */
private AuthCredential getCredentialForProvider(
String provider, String authToken, String authSecret) {
if (provider.startsWith("oidc.")) {
return OAuthProvider.newCredentialBuilder(provider).setIdToken(authToken).build();
}

switch (provider) {
case "facebook.com":
return FacebookAuthProvider.getCredential(authToken);
Expand Down
28 changes: 28 additions & 0 deletions packages/auth/e2e/provider.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,4 +226,32 @@ describe('auth() -> Providers', function () {
});
});
});

describe('OIDCAuthProvider', function () {
describe('constructor', function () {
it('should throw an unsupported error', function () {
(() => new firebase.auth.OIDCAuthProvider()).should.throw(
'`new OIDCAuthProvider()` is not supported on the native Firebase SDKs.',
);
});
});

describe('credential', function () {
it('should return a credential object', function () {
const token = '123456';
const secret = '654321';
const providerSuffix = 'sample-provider';
const credential = firebase.auth.OIDCAuthProvider.credential(providerSuffix, token, secret);
credential.providerId.should.equal('oidc.' + providerSuffix);
credential.token.should.equal(token);
credential.secret.should.equal(secret);
});
});

describe('PROVIDER_ID', function () {
it('should return oidc.', function () {
firebase.auth.OIDCAuthProvider.PROVIDER_ID.should.equal('oidc.');
});
});
});
});
4 changes: 4 additions & 0 deletions packages/auth/ios/RNFBAuth/RNFBAuthModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,10 @@ - (FIRAuthCredential *)getCredentialForProvider:(NSString *)provider
credential = [FIROAuthProvider credentialWithProviderID:@"oauth"
IDToken:authToken
accessToken:authTokenSecret];
} else if ([provider hasPrefix:@"oidc."]) {
credential = [FIROAuthProvider credentialWithProviderID:provider
IDToken:authToken
rawNonce:nil];
} else {
DLog(@"Provider not yet handled: %@", provider);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/auth/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import FacebookAuthProvider from './providers/FacebookAuthProvider';
import GithubAuthProvider from './providers/GithubAuthProvider';
import GoogleAuthProvider from './providers/GoogleAuthProvider';
import OAuthProvider from './providers/OAuthProvider';
import OIDCAuthProvider from './providers/OIDCAuthProvider';
import PhoneAuthProvider from './providers/PhoneAuthProvider';
import PhoneMultiFactorGenerator from './PhoneMultiFactorGenerator';
import TwitterAuthProvider from './providers/TwitterAuthProvider';
Expand All @@ -54,6 +55,7 @@ const statics = {
FacebookAuthProvider,
PhoneMultiFactorGenerator,
OAuthProvider,
OIDCAuthProvider,
PhoneAuthState: {
CODE_SENT: 'sent',
AUTO_VERIFY_TIMEOUT: 'timeout',
Expand Down
36 changes: 36 additions & 0 deletions packages/auth/lib/providers/OIDCAuthProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2016-present Invertase Limited & Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this library except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

const providerId = 'oidc.';

export default class OIDCAuthProvider {
constructor() {
throw new Error('`new OIDCAuthProvider()` is not supported on the native Firebase SDKs.');
}

static get PROVIDER_ID() {
return providerId;
}

static credential(oidcSuffix, idToken, accessToken) {
return {
token: idToken,
secret: accessToken,
providerId: providerId + oidcSuffix,
};
}
}

1 comment on commit 469bf00

@vercel
Copy link

@vercel vercel bot commented on 469bf00 Jan 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.