@@ -15,21 +15,26 @@ export class AuthInterceptorGenerator {
1515 * @param parser The `SwaggerParser` instance for accessing spec details.
1616 * @param project The `ts-morph` project for AST manipulation.
1717 */
18- constructor ( private parser : SwaggerParser , private project : Project ) { }
18+ constructor ( private parser : SwaggerParser , private project : Project ) { }
1919
2020 /**
21- * Generates the auth interceptor file if any security schemes are defined in the spec.
22- * It analyzes the schemes to determine which tokens (API key, Bearer) are needed and
23- * generates the corresponding injection logic.
21+ * Generates the auth interceptor file if any **supported** security schemes are defined in the spec.
22+ * A scheme is supported if it's an `apiKey` in the header/query or an `http`/`oauth2` bearer token.
2423 *
2524 * @param outputDir The root output directory.
26- * @returns An object containing the names of the tokens used (e.g., `['apiKey', 'bearerToken']`),
27- * or `void` if no security schemes are found and no file is generated.
25+ * @returns An object containing the names of the tokens for supported schemes (e.g., `['apiKey', 'bearerToken']`),
26+ * or `void` if no supported security schemes are found and no file is generated.
2827 */
2928 public generate ( outputDir : string ) : { tokenNames : string [ ] } | void {
3029 const securitySchemes = Object . values ( this . parser . getSecuritySchemes ( ) ) ;
31- if ( securitySchemes . length === 0 ) {
32- return ; // Don't generate if no security schemes are defined.
30+
31+ // FIX: Determine which types of authentication are SUPPORTED by this interceptor.
32+ const hasSupportedApiKey = securitySchemes . some ( s => s . type === 'apiKey' && ( s . in === 'header' || s . in === 'query' ) ) ;
33+ const hasBearer = securitySchemes . some ( s => ( s . type === 'http' && s . scheme === 'bearer' ) || s . type === 'oauth2' ) ;
34+
35+ // If no supported schemes are found, do not generate the file at all.
36+ if ( ! hasSupportedApiKey && ! hasBearer ) {
37+ return ;
3338 }
3439
3540 const authDir = path . join ( outputDir , 'auth' ) ;
@@ -38,13 +43,10 @@ export class AuthInterceptorGenerator {
3843
3944 sourceFile . insertText ( 0 , UTILITY_GENERATOR_HEADER_COMMENT ) ;
4045
41- const hasApiKey = securitySchemes . some ( s => s . type === 'apiKey' ) ;
42- const hasBearer = securitySchemes . some ( s => ( s . type === 'http' && s . scheme === 'bearer' ) || s . type === 'oauth2' ) ;
43-
4446 const tokenImports : string [ ] = [ ] ;
4547 const tokenNames : string [ ] = [ ] ; // This will be the return value
4648
47- if ( hasApiKey ) {
49+ if ( hasSupportedApiKey ) {
4850 tokenImports . push ( 'API_KEY_TOKEN' ) ;
4951 tokenNames . push ( 'apiKey' ) ;
5052 }
@@ -54,28 +56,42 @@ export class AuthInterceptorGenerator {
5456 }
5557
5658 sourceFile . addImportDeclarations ( [
57- { moduleSpecifier : '@angular/common/http' , namedImports : [ 'HttpEvent' , 'HttpHandler' , 'HttpInterceptor' , 'HttpRequest' ] } ,
59+ {
60+ moduleSpecifier : '@angular/common/http' ,
61+ namedImports : [ 'HttpEvent' , 'HttpHandler' , 'HttpInterceptor' , 'HttpRequest' ] ,
62+ } ,
5863 { moduleSpecifier : '@angular/core' , namedImports : [ 'inject' , 'Injectable' ] } ,
5964 { moduleSpecifier : 'rxjs' , namedImports : [ 'Observable' ] } ,
60- { moduleSpecifier : './auth.tokens' , namedImports : tokenImports }
65+ { moduleSpecifier : './auth.tokens' , namedImports : tokenImports } ,
6166 ] ) ;
6267
6368 const interceptorClass = sourceFile . addClass ( {
6469 name : `AuthInterceptor` ,
6570 isExported : true ,
6671 decorators : [ { name : 'Injectable' , arguments : [ `{ providedIn: 'root' }` ] } ] ,
6772 implements : [ 'HttpInterceptor' ] ,
68- docs : [ " Intercepts HTTP requests to apply authentication credentials based on OpenAPI security schemes." ]
73+ docs : [ ' Intercepts HTTP requests to apply authentication credentials based on OpenAPI security schemes.' ] ,
6974 } ) ;
7075
71- if ( hasApiKey ) {
72- interceptorClass . addProperty ( { name : 'apiKey' , isReadonly : true , scope : Scope . Private , type : 'string | null' , initializer : `inject(API_KEY_TOKEN, { optional: true })` } ) ;
76+ if ( hasSupportedApiKey ) {
77+ interceptorClass . addProperty ( {
78+ name : 'apiKey' ,
79+ isReadonly : true ,
80+ scope : Scope . Private ,
81+ type : 'string | null' ,
82+ initializer : `inject(API_KEY_TOKEN, { optional: true })` ,
83+ } ) ;
7384 }
7485 if ( hasBearer ) {
75- interceptorClass . addProperty ( { name : 'bearerToken' , isReadonly : true , scope : Scope . Private , type : '(string | (() => string)) | null' , initializer : `inject(BEARER_TOKEN_TOKEN, { optional: true })` } ) ;
86+ interceptorClass . addProperty ( {
87+ name : 'bearerToken' ,
88+ isReadonly : true ,
89+ scope : Scope . Private ,
90+ type : '(string | (() => string)) | null' ,
91+ initializer : `inject(BEARER_TOKEN_TOKEN, { optional: true })` ,
92+ } ) ;
7693 }
7794
78- // FIX: The logic is refactored to chain clones correctly, preventing overwrites.
7995 let statementsBody = 'let authReq = req;' ;
8096
8197 const uniqueSchemes = Array . from ( new Set ( securitySchemes . map ( s => JSON . stringify ( s ) ) ) ) . map ( s => JSON . parse ( s ) ) ;
@@ -88,7 +104,6 @@ export class AuthInterceptorGenerator {
88104 statementsBody += `\nif (this.apiKey) { authReq = authReq.clone({ setParams: { ...authReq.params.keys().reduce((acc, key) => ({ ...acc, [key]: authReq.params.getAll(key) }), {}), '${ scheme . name } ': this.apiKey } }); }` ;
89105 }
90106 } else if ( ( scheme . type === 'http' && scheme . scheme === 'bearer' ) || scheme . type === 'oauth2' ) {
91- // De-duplication check for bearer tokens
92107 if ( ! statementsBody . includes ( 'this.bearerToken' ) ) {
93108 statementsBody += `\nif (this.bearerToken) { const token = typeof this.bearerToken === 'function' ? this.bearerToken() : this.bearerToken; if (token) { authReq = authReq.clone({ setHeaders: { ...authReq.headers.keys().reduce((acc, key) => ({ ...acc, [key]: authReq.headers.getAll(key) }), {}), 'Authorization': \`Bearer \${token}\` } }); } }` ;
94109 }
0 commit comments