+ - 0:00:00
Notes for current slide
Notes for next slide

Authentication and authorization in plumber with the sealr package

Frie Preu
(@ameisen_strasse)

codecentric AG

1 / 26

sealr co-author: Jan Dix


toll_patsch

2 / 26

The why

3 / 26

The why

Security should must be easy

3 / 26

not should but must. in my experience security measures are not adopted even IF easy.

Examples

4 / 26

Examples

  • password security
4 / 26

Examples

  • password security
  • 2FA authentication
4 / 26

Examples

  • password security
  • 2FA authentication
  • PGP encryption
  • disk encryption
  • ...
4 / 26

Examples

  • password security
  • 2FA authentication
  • PGP encryption
  • disk encryption
  • ...
4 / 26

Securing plumber APIs?

  • many dimensions1, here: focus on Authorization and Authentication

1https://www.rplumber.io/docs/security.html

5 / 26

Authentication

verifying an identity

Image by Andreas Lischka from Pixabay

--> plumber API: check credentials (e.g. login)

6 / 26

Authentication

verifying an identity

Image by Andreas Lischka from Pixabay

--> plumber API: check credentials (e.g. login)

Authorization

verifying access rights / permissions to do something

Image by Nina Garman from Pixabay

--> plumber API: checking permissions to access endpoint(s) xyz

6 / 26

sealr mission

The goal of sealr is to provide multiple authentication and authorization strategies for plumber by using filters. In doing so, we hope to make best practices in authentication easy to implement for the R community.1

7 / 26

How?

9 / 26

How?

sealr features and implementation

9 / 26

passport.js

10 / 26

passport.js

Consistent interface

11 / 26

passport.js

Consistent interface

  1. specify and configure strategy Object, e.g. FacebookStrategy
    • optionally: write custom code for checking authentication
11 / 26

passport.js

Consistent interface

  1. specify and configure strategy Object, e.g. FacebookStrategy
    • optionally: write custom code for checking authentication
  2. use passport.authenticate(), e.g. passport.authenticate('facebook') to authenticate calls to endpoints
11 / 26

Example

12 / 26

R Memes for Statistical Fiends

Ever wanted to have R Memes at the ready whenever you want? Of course not. But here is a package that does exactly that.

13 / 26

17 / 26

17 / 26

17 / 26

Strategy configuration

  • not implemented in sealr because unique to use case
  • custom code needed
  • but examples in /examples folder & in docs!
18 / 26

rstatsmemes: strategy configuration

pr$handle("POST", "/authentication", function (req, res, user = NULL, password = NULL) {
# check if user provided credentials
if (is.null(user) || is.null(password)) {
res$status <- 404
return(sealr::is_authed_return_list(FALSE, "Failed.", 404, "User or password wrong."))
}
# find user in database
index <- match(user, users$user)
# check if user exist
if (is.na(index)) {
res$status <- 401
return(sealr::is_authed_return_list(FALSE, "Failed.", 401, "User or password wrong."))
}
# check if password is correct
if (!bcrypt::checkpw(password, users$password[index])){
res$status <- 401
return(sealr::is_authed_return_list(FALSE, "Failed.", 401, "User or password wrong."))
}
19 / 26
# define jwt payload
# information about the additional fields can be found at
# https://tools.ietf.org/html/rfc7519#section-4.1
payload <- jose::jwt_claim(userID = users$id[index])
# convert secret to bytes
secret_raw <- charToRaw(secret)
# encode token using the secret
jwt <- jose::jwt_encode_hmac(payload, secret = secret_raw)
# return jwt as response
return(jwt = jwt)
}, preempt = c("auth"))
20 / 26

rstatsmemes: sealr authenticate

  • goal: protect /meme/<no>/stats, leave /meme unprotected
21 / 26

rstatsmemes: sealr authenticate

  • goal: protect /meme/<no>/stats, leave /meme unprotected

  • filter(s): "[...] “pipeline” for handling incoming requests."1

21 / 26

rstatsmemes: sealr authenticate

  • goal: protect /meme/<no>/stats, leave /meme unprotected

  • filter(s): "[...] “pipeline” for handling incoming requests."1

pr$filter("auth", function (req, res) {
sealr::authenticate(req = req, # plumber object: the request from the user
res = res, # plumber object: the response list
is_authed_fun = sealr::is_authed_jwt, # checker fun
token_location = "header", secret = secret)
# arguments passed to the checker function
})
21 / 26

rstatsmemes: sealr authenticate

  • goal: protect /meme/<no>/stats, leave /meme unprotected

  • filter(s): "[...] “pipeline” for handling incoming requests."1

pr$filter("auth", function (req, res) {
sealr::authenticate(req = req, # plumber object: the request from the user
res = res, # plumber object: the response list
is_authed_fun = sealr::is_authed_jwt, # checker fun
token_location = "header", secret = secret)
# arguments passed to the checker function
})
  • authenticate: small wrapper function.
21 / 26

rstatsmemes: sealr authenticate

  • goal: protect /meme/<no>/stats, leave /meme unprotected

  • filter(s): "[...] “pipeline” for handling incoming requests."1

pr$filter("auth", function (req, res) {
sealr::authenticate(req = req, # plumber object: the request from the user
res = res, # plumber object: the response list
is_authed_fun = sealr::is_authed_jwt, # checker fun
token_location = "header", secret = secret)
# arguments passed to the checker function
})
  • authenticate: small wrapper function.
  • is_authed_fun (checker function): check whether authed
    • is_authed_jwt: can the JWT be decrypted, ...?
    • (is_authed_oidc_google: is this JWT a valid Google JWT, ...?)

1https://www.rplumber.io/docs/routing-and-input.html#filters

21 / 26

rstatsmemes: Protect endpoints

pr$handle("GET", "/meme/<no>/stats", function(req, res, no){
no <- as.integer(no)
stats <- memes %>%
dplyr::filter(meme_number == no) %>%
dplyr::slice(1) %>% # make sure only one is returend
dplyr::select(meme_number, shares_count, likes_count, comments_count)
return(as.list(stats))
}, serializer = plumber::serializer_unboxed_json())
22 / 26

rstatsmemes: Protect endpoints

pr$handle("GET", "/meme/<no>/stats", function(req, res, no){
no <- as.integer(no)
stats <- memes %>%
dplyr::filter(meme_number == no) %>%
dplyr::slice(1) %>% # make sure only one is returend
dplyr::select(meme_number, shares_count, likes_count, comments_count)
return(as.list(stats))
}, serializer = plumber::serializer_unboxed_json())
pr$handle("GET", "/meme", function(req, res){
# get a random meme
random_meme <- memes %>%
dplyr::sample_n(1) %>%
dplyr::select(meme_number, message, full_picture)
return(as.list(random_meme))
}, serializer = plumber::serializer_unboxed_json(),
preempt = c("auth")
)
22 / 26

Outlook

23 / 26

Outlook

Plans for sealr

23 / 26

Plans for sealr

short-term

  • finish / polish docs
  • extend / another "real world" implementation
24 / 26

Plans for sealr

short-term

  • finish / polish docs
  • extend / another "real world" implementation

mid- / long-term

  • develop more strategies
  • provide more examples for strategy configuration (/authentication)
  • provide tooling (docker image...)
24 / 26

How you can help

Talk to us! 💬

  • use cases + real world set-ups
25 / 26

How you can help

Talk to us! 💬

  • use cases + real world set-ups

Try sealr!

  • keep in mind the disclaimer!
  • file issues / feature requests on GitHub
25 / 26

How you can help

Talk to us! 💬

  • use cases + real world set-ups

Try sealr!

  • keep in mind the disclaimer!
  • file issues / feature requests on GitHub

Write/review code

  • review code / docs
  • write code / docs
  • provide examples
25 / 26

All you need to know!

Frie Preu

Jan Dix

26 / 26

sealr co-author: Jan Dix


toll_patsch

2 / 26
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow