Skip to content

Commit 70adf6b

Browse files
committed
Minor changes to Refresh token
1 parent 0a55186 commit 70adf6b

File tree

6 files changed

+140
-17
lines changed

6 files changed

+140
-17
lines changed

packages/auth/demo/public/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,7 @@
853853
</div>
854854
<div class="tab-pane" id="tab-byo-ciam-content">
855855
<h2>Sign in with your CIAM token</h2>
856+
<div id="firebase-token-status">No CIAM token found. User not logged in.</div>
856857
<input type="text" id="byo-ciam-token"
857858
class="form-control" placeholder="Enter CIAM token" />
858859
<button class="btn btn-block btn-primary"

packages/auth/demo/src/index.js

Lines changed: 119 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,113 @@ async function getActiveUserBlocking() {
149149
}
150150
}
151151

152+
class RefreshIdpTokenResult {
153+
idpConfigId;
154+
idToken;
155+
}
156+
152157
class TokenRefreshHandlerImpl {
158+
/**
159+
* Opens a popup to get a 3P ID token and Config ID from the user.
160+
* @returns {Promise<RefreshIdpTokenResult>} A promise that resolves with the result object.
161+
*/
153162
refreshToken() {
154-
log("inside here");
155-
console.log("inside handler");
156-
return;
163+
log('inside here');
164+
console.log('inside handler - opening popup for 3p token');
165+
166+
// This function handles the popup logic and returns the required object
167+
return this.promptForTokenAndConfigId();
168+
}
169+
170+
/**
171+
* Opens a Bootstrap modal to ask the user for an ID token and IDP Config ID.
172+
*
173+
* This function dynamically creates a modal, shows it, and waits for
174+
* user input. It returns a Promise that resolves or rejects based
175+
* on the user's action.
176+
*
177+
* @returns {Promise<RefreshIdpTokenResult>} A promise that resolves with the
178+
* RefreshIdpTokenResult object, or rejects if the user cancels.
179+
*/
180+
promptForTokenAndConfigId() {
181+
// We return a Promise that will be resolved/rejected by the modal's buttons
182+
return new Promise((resolve, reject) => {
183+
// A flag to track if the promise has been settled
184+
let isSubmitted = false;
185+
const modalId = 'third-party-token-modal';
186+
187+
// 1. Define Modal HTML with two input fields
188+
const modalHtml = `
189+
<div class="modal fade" id="${modalId}" tabindex="-1" role="dialog" aria-labelledby="tokenModalLabel">
190+
<div class="modal-dialog" role="document">
191+
<div class="modal-content">
192+
<div class="modal-header">
193+
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
194+
<span aria-hidden="true">&times;</span>
195+
</button>
196+
<h4 class="modal-title" id="tokenModalLabel">Enter 3P Token Details</h4>
197+
</div>
198+
<div class="modal-body">
199+
<p>Please enter the third-party token details:</p>
200+
<div class="form-group">
201+
<label for="idp-config-id-input-field">IDP Config ID</label>
202+
<input type="text" class="form-control" id="idp-config-id-input-field" placeholder="eg: idp.example.com">
203+
</div>
204+
<div class="form-group">
205+
<label for="id-token-input-field">ID Token</label>
206+
<input type="text" class="form-control" id="id-token-input-field" placeholder="Paste ID Token here">
207+
</div>
208+
</div>
209+
<div class="modal-footer">
210+
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
211+
<button type="button" class="btn btn-primary" id="token-submit-btn">Submit</button>
212+
</div>
213+
</div>
214+
</div>
215+
</div>
216+
`;
217+
218+
// 2. Append modal to body and get a jQuery reference
219+
$('body').append(modalHtml);
220+
const $modal = $(`#${modalId}`);
221+
222+
// 3. Setup Event Handlers
223+
224+
// Handle Submit button click
225+
$modal.find('#token-submit-btn').on('click', () => {
226+
isSubmitted = true;
227+
228+
// Read values from *both* input fields
229+
const configId = $modal.find('#idp-config-id-input-field').val();
230+
const token = $modal.find('#id-token-input-field').val();
231+
232+
$modal.modal('hide'); // Hide the modal
233+
234+
// Create the result object as requested
235+
const result = new RefreshIdpTokenResult();
236+
result.idpConfigId = configId;
237+
result.idToken = token;
238+
239+
resolve(result); // Resolve the promise with the object
240+
});
241+
242+
// Handle modal being closed (by 'x', 'Cancel' button, backdrop click, or ESC)
243+
$modal.on('hidden.bs.modal', () => {
244+
$modal.remove(); // Clean up the modal from the DOM
245+
246+
// If the modal was hidden *without* submitting, reject the promise
247+
if (!isSubmitted) {
248+
reject(new Error('User cancelled token input.'));
249+
}
250+
});
251+
252+
// 4. Show the modal
253+
$modal.modal('show');
254+
});
157255
}
158256
}
159257

258+
160259
/**
161260
* Refreshes the current user data in the UI, displaying a user info box if
162261
* a user is signed in, or removing it.
@@ -1528,16 +1627,17 @@ async function exchangeCIAMToken(token) {
15281627
function onExchangeToken(event) {
15291628
event.preventDefault();
15301629
const byoCiamInput = document.getElementById('byo-ciam-token');
1531-
const byoCiamResult = document.getElementById('byo-ciam-result');
1630+
const firebaseTokenStatus = document.getElementById('firebase-token-status');
15321631

1533-
byoCiamResult.textContent = 'Exchanging token...';
1632+
firebaseTokenStatus.textContent = 'Exchanging token...';
15341633

15351634
exchangeCIAMToken(byoCiamInput.value)
15361635
.then(response => {
1537-
byoCiamResult.textContent = response;
1636+
firebaseTokenStatus.textContent = '✅ Firebase token is set: ' + response;
15381637
console.log('Token:', response);
15391638
})
15401639
.catch(error => {
1640+
(firebaseTokenStatus.textContent = 'Error exchanging token: '), error;
15411641
console.error('Error exchanging token:', error);
15421642
});
15431643
}
@@ -2101,6 +2201,19 @@ function initApp() {
21012201
const tokenRefreshHandler = new TokenRefreshHandlerImpl();
21022202
regionalAuth.setTokenRefreshHandler(tokenRefreshHandler);
21032203

2204+
const firebaseTokenStatus = document.getElementById('firebase-token-status');
2205+
setTimeout(() => {
2206+
if (regionalAuth.firebaseToken) {
2207+
firebaseTokenStatus.textContent =
2208+
'✅ Firebase token is set: ' + regionalAuth.firebaseToken.token;
2209+
} else {
2210+
firebaseTokenStatus.textContent =
2211+
'No CIAM token found. User not logged in.';
2212+
}
2213+
console.log('firebaseToken after delay: ', regionalAuth.firebaseToken);
2214+
}, 1000);
2215+
2216+
21042217
tempApp = initializeApp(
21052218
{
21062219
apiKey: config.apiKey,

packages/auth/src/core/auth/auth_impl.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ import {
3838
Unsubscribe,
3939
PasswordValidationStatus,
4040
TenantConfig,
41-
FirebaseToken
41+
FirebaseToken,
42+
TokenRefreshHandler,
43+
RefreshIdpTokenResult
4244
} from '../../model/public_types';
4345
import {
4446
createSubscribe,
@@ -49,7 +51,7 @@ import {
4951
Subscribe
5052
} from '@firebase/util';
5153

52-
import { AuthInternal, ConfigInternal, TokenRefreshHandler } from '../../model/auth';
54+
import { AuthInternal, ConfigInternal} from '../../model/auth';
5355
import { PopupRedirectResolverInternal } from '../../model/popup_redirect';
5456
import { UserInternal } from '../../model/user';
5557
import {
@@ -85,14 +87,15 @@ import { PasswordPolicyInternal } from '../../model/password_policy';
8587
import { PasswordPolicyImpl } from './password_policy_impl';
8688
import { getAccountInfo } from '../../api/account_management/account';
8789
import { UserImpl } from '../user/user_impl';
90+
import { exchangeToken } from '../strategies/exhange_token';
8891

8992
interface AsyncAction {
9093
(): Promise<void>;
9194
}
9295

9396
export const enum DefaultConfig {
9497
TOKEN_API_HOST = 'securetoken.googleapis.com',
95-
API_HOST = 'identitytoolkit.googleapis.com',
98+
API_HOST = 'staging-identitytoolkit.sandbox.googleapis.com',
9699
API_SCHEME = 'https',
97100
// TODO(sammansi): Update the endpoint before BYO-CIAM Private Preview Release.
98101
REGIONAL_API_HOST = 'autopush-identityplatform.sandbox.googleapis.com/v2alpha/'
@@ -102,7 +105,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
102105
currentUser: User | null = null;
103106
emulatorConfig: EmulatorConfig | null = null;
104107
firebaseToken: FirebaseToken | null = null;
105-
tokenRefreshHandler?: TokenRefreshHandler;
108+
private tokenRefreshHandler?: TokenRefreshHandler;
106109
private operations = Promise.resolve();
107110
private persistenceManager?: PersistenceUserManager;
108111
private redirectPersistenceManager?: PersistenceUserManager;
@@ -236,7 +239,9 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
236239
await this._updateFirebaseToken(null);
237240
// Awaits for the callback method to execute. The callback method
238241
// is responsible for performing the exchangeToken(auth, valid3pIdpToken)
239-
await this.tokenRefreshHandler.refreshToken();
242+
const result: RefreshIdpTokenResult = await this.tokenRefreshHandler.refreshIdpToken();
243+
_assert(result.idToken && result.idpConfigId, AuthErrorCode.INVALID_CREDENTIAL);
244+
await exchangeToken(this, result.idpConfigId, result.idToken);
240245
return this.getFirebaseAccessToken(false);
241246
}
242247

packages/auth/src/core/auth/firebase_internal.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class AuthInterop implements FirebaseAuthInternal {
5858
'Refresh token is not a valid operation for Regional Auth instance initialized.'
5959
);
6060
}
61-
const firebaseAccessToken = (await this.auth.getFirebaseAccessToken())?.token
61+
const firebaseAccessToken = (await this.auth.getFirebaseAccessToken())?.token;
6262
if (!firebaseAccessToken) {
6363
return null;
6464
}

packages/auth/src/model/auth.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,6 @@ export interface ConfigInternal extends Config {
5858
clientPlatform: ClientPlatform;
5959
}
6060

61-
export interface TokenRefreshHandler {
62-
refreshToken(): void | Promise<void>
63-
}
64-
6561
/**
6662
* UserInternal and AuthInternal reference each other, so both of them are included in the public typings.
6763
* In order to exclude them, we mark them as internal explicitly.
@@ -71,7 +67,6 @@ export interface TokenRefreshHandler {
7167
export interface AuthInternal extends Auth {
7268
currentUser: User | null;
7369
emulatorConfig: EmulatorConfig | null;
74-
tokenRefreshHandler?: TokenRefreshHandler;
7570
_agentRecaptchaConfig: RecaptchaConfig | null;
7671
_tenantRecaptchaConfigs: Record<string, RecaptchaConfig>;
7772
_projectPasswordPolicy: PasswordPolicy | null;

packages/auth/src/model/public_types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ export interface Auth {
210210
* @param persistence - The {@link Persistence} to use.
211211
*/
212212
setPersistence(persistence: Persistence): Promise<void>;
213+
setTokenRefreshHandler(tokenRefreshHandler: TokenRefreshHandler): void;
213214
/**
214215
* The {@link Auth} instance's language code.
215216
*
@@ -360,6 +361,14 @@ export interface Persistence {
360361
readonly type: 'SESSION' | 'LOCAL' | 'NONE' | 'COOKIE';
361362
}
362363

364+
export interface TokenRefreshHandler {
365+
refreshIdpToken(): Promise<RefreshIdpTokenResult>
366+
}
367+
export interface RefreshIdpTokenResult {
368+
idpConfigId: string;
369+
idToken: string;
370+
}
371+
363372
/**
364373
* Interface representing ID token result obtained from {@link User.getIdTokenResult}.
365374
*

0 commit comments

Comments
 (0)