1- import { Component , Inject } from '@angular/core'
1+ import { ComponentType } from '@angular/cdk/overlay'
2+ import { Component , Inject , ViewChild } from '@angular/core'
23import { cloneDeep } from 'lodash'
34import { Observable , forkJoin , NEVER , of } from 'rxjs'
4- import { first , map , switchMap , take , tap } from 'rxjs/operators'
5+ import {
6+ filter ,
7+ finalize ,
8+ first ,
9+ map ,
10+ switchMap ,
11+ take ,
12+ tap ,
13+ } from 'rxjs/operators'
514import { InterstitialsService } from 'src/app/cdk/interstitials/interstitials.service'
615import { PlatformInfo , PlatformInfoService } from 'src/app/cdk/platform-info'
716import { WINDOW } from 'src/app/cdk/window'
817import { UserService } from 'src/app/core'
918import { ErrorHandlerService } from 'src/app/core/error-handler/error-handler.service'
1019import { GoogleTagManagerService } from 'src/app/core/google-tag-manager/google-tag-manager.service'
20+ import { LoginMainInterstitialsManagerService } from 'src/app/core/login-interstitials-manager/login-main-interstitials-manager.service'
1121import { RecordEmailsService } from 'src/app/core/record-emails/record-emails.service'
22+ import { RecordService } from 'src/app/core/record/record.service'
1223import { TogglzService } from 'src/app/core/togglz/togglz.service'
1324import { ERROR_REPORT } from 'src/app/errors'
1425import { EmailsEndpoint , RequestInfoForm } from 'src/app/types'
26+ import { UserRecord } from 'src/app/types/record.local'
27+ import { UserSession } from 'src/app/types/session.local'
28+ import { CdkPortalOutlet , ComponentPortal , Portal } from '@angular/cdk/portal'
1529
1630@Component ( {
1731 templateUrl : './authorize.component.html' ,
1832 styleUrls : [ './authorize.component.scss' ] ,
1933 preserveWhitespaces : true ,
2034} )
2135export class AuthorizeComponent {
36+ @ViewChild ( 'interstitialOutlet' , { static : false , read : CdkPortalOutlet } )
37+ outlet ! : CdkPortalOutlet
38+
2239 redirectUrl : string
2340
2441 platform : PlatformInfo
@@ -38,58 +55,71 @@ export class AuthorizeComponent {
3855
3956 // User session properties
4057 isNotImpersonating = false
41- insidePopUpWindows = false
42- redirectByReportAlreadyAuthorize = false
58+ interstitialComponent : ComponentType < any >
59+ redirectByReportAlreadyAuthorize : boolean
4360
4461 constructor (
4562 private userService : UserService ,
4663 private platformInfoService : PlatformInfoService ,
47- private recordEmailsService : RecordEmailsService ,
48- private togglzService : TogglzService ,
49- private interstitialsService : InterstitialsService ,
5064 @Inject ( WINDOW ) private window : Window ,
5165 private googleTagManagerService : GoogleTagManagerService ,
52- private errorHandlerService : ErrorHandlerService
66+ private errorHandlerService : ErrorHandlerService ,
67+ private recordService : RecordService ,
68+ private loginMainInterstitialsManagerService : LoginMainInterstitialsManagerService
5369 ) { }
5470
5571 /**
5672 * Lifecycle hook. Initiates data loading and handles session logic.
5773 */
5874 ngOnInit ( ) : void {
5975 this . loading = true
60- this . insidePopUpWindows = ! ! this . window . opener
6176
6277 forkJoin ( {
63- userSession : this . loadUserSession ( ) ,
6478 platform : this . loadPlatformInfo ( ) ,
65- togglz : this . loadTogglzState ( ) ,
66- emails : this . loadEmails ( ) ,
79+ userSession : this . loadUserSession ( ) ,
80+ userRecord : this . recordService . getRecord ( { } ) . pipe (
81+ filter ( ( userRecord : UserRecord ) => {
82+ return (
83+ ! ! userRecord &&
84+ ! ! userRecord ?. userInfo &&
85+ ! ! userRecord ?. emails &&
86+ ! ! userRecord ?. affiliations ?. length
87+ )
88+ } ) ,
89+ take ( 1 )
90+ ) ,
6791 } )
6892 . pipe (
69- switchMap ( ( results ) => {
70- if ( ! results . userSession ?. userInfo ) {
71- return of ( results )
72- } else {
73- return this . loadInterstitialViewed ( ) . pipe (
74- map ( ( ) => {
75- return results
93+ switchMap ( ( record ) => {
94+ return this . loginMainInterstitialsManagerService
95+ . checkLoginInterstitials ( record . userRecord , {
96+ returnType : 'component' ,
97+ togglzPrefix : 'OAUTH' ,
98+ } )
99+ . pipe (
100+ take ( 1 ) ,
101+ map ( ( interstitial ) => {
102+ this . interstitialComponent = interstitial
103+ } ) ,
104+ switchMap ( ( ) => {
105+ return of ( record . userSession )
106+ } ) ,
107+ finalize ( ( ) => {
108+ this . handleUserSession ( record . userSession )
76109 } )
77110 )
78- }
79111 } )
80112 )
81- . subscribe ( ( { userSession } ) => {
82- this . handleUserSession ( userSession )
83- } )
113+ . subscribe ( )
84114 }
85115
86116 /**
87117 * Called by template to handle final redirection.
88118 */
89119 handleRedirect ( url : string ) : void {
90120 this . redirectUrl = url
91- if ( url && this . canShowDomainInterstitial ( ) ) {
92- this . showDomainInterstitial ( )
121+ if ( this . redirectUrl && this . isThereInterstitialToShow ( ) ) {
122+ this . showInterstitial ( )
93123 } else {
94124 this . finishRedirect ( )
95125 }
@@ -119,35 +149,34 @@ export class AuthorizeComponent {
119149 }
120150
121151 /**
122- * Determines whether the domain interstitial should be displayed
123- * based on user domain status, togglz, impersonation, etc.
152+ * Determines whether a interstitial should be displayed
124153 */
125- private canShowDomainInterstitial ( ) : boolean {
126- return (
127- this . hasPrivateDomains &&
128- ! this . hasPublicDomains &&
129- this . isOAuthDomainsInterstitialEnabled &&
130- ! this . hasDomainInterstitialBeenViewed &&
131- this . isNotImpersonating &&
132- ! this . insidePopUpWindows
133- )
154+ private isThereInterstitialToShow ( ) : boolean {
155+ return ! ! this . interstitialComponent
134156 }
135157
136158 /**
137- * Displays the domain interstitial and marks it as viewed.
159+ * Displays the interstitial
138160 */
139- private showDomainInterstitial ( ) : void {
161+ private showInterstitial ( ) : void {
162+ const portal = new ComponentPortal ( this . interstitialComponent )
163+
164+ const componentRef = this . outlet . attachComponentPortal ( portal )
165+
166+ componentRef . instance . finish . subscribe ( ( ) => {
167+ this . finishRedirect ( )
168+ } )
169+
170+ componentRef . changeDetectorRef . detectChanges ( )
171+
140172 this . showAuthorizationComponent = false
141173 this . showInterstital = true
142- this . interstitialsService
143- . setInterstitialsViewed ( 'DOMAIN_INTERSTITIAL' )
144- . subscribe ( )
145174 }
146175
147176 /**
148177 * Loads the user session data.
149178 */
150- private loadUserSession ( ) : Observable < any > {
179+ private loadUserSession ( ) : Observable < UserSession > {
151180 return this . userService . getUserSession ( ) . pipe ( first ( ) )
152181 }
153182
@@ -161,91 +190,35 @@ export class AuthorizeComponent {
161190 )
162191 }
163192
164- /**
165- * Checks if the domain interstitial was previously viewed by the user.
166- */
167- private loadInterstitialViewed ( ) : Observable < boolean > {
168- return this . interstitialsService
169- . getInterstitialsViewed ( 'DOMAIN_INTERSTITIAL' )
170- . pipe (
171- tap ( ( wasViewed ) => {
172- this . hasDomainInterstitialBeenViewed = wasViewed
173- } )
174- )
175- }
176-
177- /**
178- * Loads togglz (feature flag) state for the OAuth domains interstitial feature.
179- */
180- private loadTogglzState ( ) : Observable < boolean > {
181- return this . togglzService . getStateOf ( 'OAUTH_DOMAINS_INTERSTITIAL' ) . pipe (
182- take ( 1 ) ,
183- tap ( ( state ) => ( this . isOAuthDomainsInterstitialEnabled = state ) )
184- )
185- }
186-
187- /**
188- * Loads the user emails from the backend and determines public/private domain flags.
189- */
190- private loadEmails ( ) : Observable < EmailsEndpoint > {
191- return this . userService . getUserSession ( ) . pipe (
192- take ( 1 ) ,
193- switchMap ( ( session ) => {
194- // Load emails only if user is logged in
195- if ( session . oauthSessionIsLoggedIn ) {
196- return this . recordEmailsService . getEmails ( ) . pipe (
197- first ( ) ,
198- tap ( ( emails ) => {
199- this . originalEmailsBackendCopy = cloneDeep ( emails )
200- this . hasPrivateDomains = this . userHasPrivateEmails ( emails )
201- this . hasPublicDomains = this . userHasPublicEmails ( emails )
202- } )
203- )
204- } else {
205- // If user is not logged in, return empty emails object
206- // This scenario is for users who are not logged in and the OAUTH URL is invalid
207- // Those will load this component to display the error message by the component `app-oauth-error`
208- this . originalEmailsBackendCopy = {
209- emails : [ ] ,
210- emailDomains : [ ] ,
211- errors : [ ] ,
212- }
213- return of ( { } as EmailsEndpoint )
214- }
215- } )
216- )
217- }
218-
219193 /**
220194 * After loading forkJoin data, decide on final flow:
221195 * show error, show domain interstitial, or show authorization screen.
222196 */
223- private handleUserSession ( userSession : any ) : void {
224- // Check if user is impersonating
225- this . isNotImpersonating =
226- userSession ?. userInfo ?. REAL_USER_ORCID ===
227- userSession ?. userInfo ?. EFFECTIVE_USER_ORCID
228-
197+ private handleUserSession ( userSession : UserSession ) : void {
229198 this . oauthSession = userSession . oauthSession
230199
231200 // 1. If the backend returned an error
232201 if ( this . oauthSession && this . oauthSession . error ) {
202+ this . debugLog ( `Oauth session error: ${ this . oauthSession . error } ` )
233203 this . showAuthorizationError = true
234204 this . loading = false
235205 return
236206 }
237207
238208 // 2. If the user was already authorized, we might show domain interstitial or just redirect
239209 if ( this . isUserAlreadyAuthorized ( this . oauthSession ) ) {
240- if ( this . canShowDomainInterstitial ( ) ) {
210+ this . debugLog ( 'User alreay authorized this app' )
211+ if ( this . isThereInterstitialToShow ( ) ) {
241212 this . redirectByReportAlreadyAuthorize = true
242- this . showDomainInterstitial ( )
213+ this . showInterstitial ( )
243214 this . loading = false
244215 this . redirectUrl = this . oauthSession . redirectUrl
245216 } else {
246217 this . finishRedirectObs ( this . oauthSession )
247218 }
248219 return
220+ } else {
221+ this . debugLog ( 'User has not authorized this app' )
249222 }
250223
251224 // 3. Otherwise, show the standard authorization component
@@ -267,21 +240,9 @@ export class AuthorizeComponent {
267240 return oauthSession . redirectUrl . includes ( oauthSession . responseType + '=' )
268241 }
269242
270- /**
271- * Helper to check if at least one email domain is public.
272- */
273- private userHasPublicEmails ( emails : EmailsEndpoint ) : boolean {
274- return ! ! emails . emailDomains ?. some (
275- ( domain ) => domain . visibility === 'PUBLIC'
276- )
277- }
278-
279- /**
280- * Helper to check if at least one email domain is private.
281- */
282- private userHasPrivateEmails ( emails : EmailsEndpoint ) : boolean {
283- return ! ! emails . emailDomains ?. some (
284- ( domain ) => domain . visibility !== 'PUBLIC'
285- )
243+ private debugLog ( message : string ) : void {
244+ if ( runtimeEnvironment . debugger ) {
245+ console . info ( '[OAuth]' , message )
246+ }
286247 }
287248}
0 commit comments