Swagger doc with plumber filter keep failed to load

I am developing an API using filter routes to check the authorization with token bearier. The API itself work well if I test it in postman. But when i run the plumber documentation by Swagger, this eror always appear. I dont want to use the authorization on swagger routes.

I have put the route condition based on solution answered by @meztez (post page), but swagger doesn't seem to be able to continue to the next endpoint with plumber::forward() function.

Is there something wrong with my script? Please welcome your input and suggestions.

This is the screenshoot of swagger documentation.

This is the script I developed.

library(jose)
library(plumber)
library(jsonlite)

#Load secret_key
{
  readRenviron(".Renviron")
  secret_key <- Sys.getenv('SECRET_KEY') 
}

 
#' @filter checkAuthentication
function(req, res){ 
  
  
  if (req$PATH_INFO %in% c("/__docs__/", "/__swagger__/", "/openapi.json")) {
    print('Run for document!')
    plumber::forward() #no need authentication for api-document, continue
  }
   
  #filter
  if (is.null(req$HTTP_TOKEN)){
    res$status <- 401
    return(list(error = 'Token not found in request header'))
  }
  
  token = req$HTTP_TOKEN
  
  tryCatch(
    expr = {
      #decode 
      payload <- jwt_decode_hmac(token, secret  = secret_key)
      print(payload)
      #Create a response message
      
      plumber::forward() #User authenticated, continue
      
    },
    error = function(e){
      #Create a message 
      msg <- "Signature verification failed!"
      res$status <- 401 # Bad request
      return(list(message=msg))
    }
  )
  
}

#* Scoring/predict data using model_scoring  
#* @get /hit_model 
function(req,res) {
  
  print('Welcome!')
  return(list(message=paste('You have an authorized. Ready to Predict Model.')))
}

Here is my R environment:

>     sessionInfo()
R version 4.2.0 (2022-04-22 ucrt)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 22631)

Matrix products: default

locale:
[1] LC_COLLATE=English_Indonesia.utf8 
[2] LC_CTYPE=English_Indonesia.utf8   
[3] LC_MONETARY=English_Indonesia.utf8
[4] LC_NUMERIC=C                      
[5] LC_TIME=English_Indonesia.utf8    

attached base packages:
[1] stats     graphics  grDevices utils     datasets 
[6] methods   base     

other attached packages:
[1] jsonlite_1.8.7 plumber_1.2.2  jose_1.2.0    
[4] openssl_2.1.1 

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.11       ps_1.7.5         
 [3] later_1.3.1       R6_2.5.1         
 [5] lifecycle_1.0.4   magrittr_2.0.3   
 [7] stringi_1.7.12    rlang_1.1.2      
 [9] cli_3.6.1         swagger_3.33.1   
[11] rstudioapi_0.15.0 promises_1.2.1   
[13] callr_3.7.3       webutils_1.1     
[15] tools_4.2.0       compiler_4.2.0   
[17] processx_3.8.2    askpass_1.2.0   

plumber::forward only set a flag in the execution context.

> plumber::forward
function () 
{
    exec <- getCurrentExec()
    exec$forward <- TRUE
}
<bytecode: 0x5a22ef6d0ee0>
<environment: namespace:plumber>

Maybe try

library(jose)
library(plumber)
library(jsonlite)

#Load secret_key
{
  readRenviron(".Renviron")
  secret_key <- Sys.getenv('SECRET_KEY') 
}

 
#' @filter checkAuthentication
function(req, res){ 
  
  
  if (req$PATH_INFO %in% c("/__docs__/", "/__swagger__/", "/openapi.json")) {
    print('Run for document!')
    plumber::forward() #no need authentication for api-document, continue
  } else {

    #filter
    if (is.null(req$HTTP_TOKEN)){
      res$status <- 401
      return(list(error = 'Token not found in request header'))
    }
  
    token = req$HTTP_TOKEN
  
    tryCatch(
      expr = {
        #decode 
        payload <- jwt_decode_hmac(token, secret  = secret_key)
        print(payload)
        #Create a response message
      
        plumber::forward() #User authenticated, continue
      
      },
      error = function(e){
        #Create a message 
        msg <- "Signature verification failed!"
        res$status <- 401 # Bad request
        return(list(message=msg))
      }
    )

  }
  
}

#* Scoring/predict data using model_scoring  
#* @get /hit_model 
function(req,res) {
  
  print('Welcome!')
  return(list(message=paste('You have an authorized. Ready to Predict Model.')))
}

Thankyou for your reply. I have try your script, now the swagger documentation can show up.
But it seems that with the addition return() function, the process does not leave the routes filter.

In the following output results, the API is still checking the token. Is there any other way to make the API read the main endpoint?

Update:
From my observations, unfortunately this plumber filter cannot be bypassed. Couse even the swagger run successfully, it will check again the filter routes when the endpoint is hit.
image

So i used to add the swagger docs input field for Authorization using this function.
It works well for me.

pr_set_api_spec()

I've modified to an if/else. I think returning something trigger another execution loop.

Thank you very much for your advice. You're right it can trigger another execution loop. Actually, it solve my first problem :). The swagger can escape the filter routes if the PATH_INFO one of this list c("/__docs__/", "/__swagger__/", "/openapi.json").

But at the end, swagger change the PATH_INFO back to original. So it will read again the filter routes.

This is the log on my console, when i hit via swagger. I've added print(req$PATH_INFO) on the first line of filter routes. It seems that this check token cannot be passed in Swagger, right?

> pr('backup/api-test_v2.R') %>%  
+   #pr_set_api_spec(add_auth) %>% 
+   pr_run(port=8002,host = '127.0.0.1',docs=T)
Running plumber API at http://127.0.0.1:8002
Running swagger Docs at http://127.0.0.1:8002/__docs__/
[1] "My path: /__docs__/"
[1] "Run for document!"
[1] "My path: /openapi.json"
[1] "Run for document!"
[1] "My path: /hit_model"
[1] "Token not found in request header" 

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.