-
Notifications
You must be signed in to change notification settings - Fork 32
/
Authentication.Rmd
188 lines (153 loc) · 5.18 KB
/
Authentication.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
---
title: "Authentication"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Authentication}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
results= 'markup'
)
```
Out of the box RestRserve provides two authentication schemas: [`Basic`](https://swagger.io/docs/specification/authentication/basic-authentication/) and [`Bearer`](https://swagger.io/docs/specification/authentication/bearer-authentication/).
## Basic
**Note that Basic authentication, should only be used over HTTPS (SSL) or within secure network.** In HTTP protocol `Authorization` header (as well as other parts of the HTTP request) are plain text and are not encrypted!
According to `AuthBackendBasic` documentation first of all we need to provide an authentication function. It should take 2 arguments - `user` and `password` and return `logical` value - whether access is allowed for a given user or not.
```{r}
library(RestRserve)
allowed_access = list(
"user-1" = "password-1",
"user-2" = "password-2"
)
auth_fun = function(user, password) {
res = FALSE
try({
res = identical(allowed_access[[user]], password)
}, silent = TRUE)
return(res)
}
```
Now we can create authentication backend.
```{r}
basic_auth_backend = AuthBackendBasic$new(FUN = auth_fun)
```
Now let's create application which requires authorization in order to use `/secure/factorial` endpoint:
```{r}
auth_mw = AuthMiddleware$new(
auth_backend = basic_auth_backend,
routes = "/secure/factorial",
id = "auth_middleware"
)
app = Application$new(middleware = list(auth_mw))
```
Let's add two endpoints - first public (`/factorial`) and second with restricted access (`/secure/factorial`):
```{r}
factorial_handler = function(.req, .res) {
x = .req$get_param_query("x")
x = as.integer(x)
.res$set_body(factorial(x))
}
app$add_get("/factorial", factorial_handler)
app$add_get("/secure/factorial", factorial_handler)
```
As we can see first endpoint doesn't require any authentication:
```{r}
req = Request$new(path = "/factorial", parameters_query = list(x = "5"))
res = app$process_request(req)
res$body
```
Let's try to send a request without credentials to the second endpoint:
```{r}
req = Request$new(path = "/secure/factorial", parameters_query = list(x = "5"))
res = app$process_request(req)
res$body
```
As expected this gives `405` error.
Now let's add correct credentials:
```{r}
credentials = jsonlite::base64_enc("user-1:password-1")
headers = list("Authorization" = sprintf("Basic %s", credentials))
req = Request$new(
path = "/secure/factorial",
parameters_query = list(x = "5"),
headers = headers
)
res = app$process_request(req)
res$body
```
Success!
Let's see what happens if password is wrong:
```{r}
credentials = jsonlite::base64_enc("user-1:password-2")
headers = list("Authorization" = sprintf("Basic %s", credentials))
req = Request$new(
path = "/secure/factorial",
parameters_query = list(x = "5"),
headers = headers
)
res = app$process_request(req)
res$body
```
## Bearer
`Bearer` authentication (also called "token" authentication) is an HTTP authentication scheme that involves security tokens called bearer tokens. The name "Bearer authentication" can be understood as "give access to the bearer of this token." The bearer token is a cryptic string, usually generated by the server in response to a login request. The client must send this token in the Authorization header when making requests to protected resources.
The `Bearer` authentication scheme was originally created as part of `OAuth 2.0` in [RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750), but is sometimes also used on its own. Similarly to Basic authentication, Bearer authentication should only be used over HTTPS (SSL).
```{r}
allowed_tokens = c(
"super_secure_token_1",
"super_secure_token_2"
)
auth_fun = function(token) {
res = FALSE
try({
res = token %in% allowed_tokens
}, silent = TRUE)
return(res)
}
basic_auth_backend = AuthBackendBearer$new(FUN = auth_fun)
```
As an alternative to requiring authentication for a single endpoint we can make it mandatory for all endpoints which start with certain pattern:
```{r}
auth_mw = AuthMiddleware$new(
auth_backend = basic_auth_backend,
routes = "/secure/",
match = "partial",
id = "auth_middleware"
)
app = Application$new(middleware = list(auth_mw))
```
For example:
```{r}
app$add_get("/hello0", function(req, res) {res$body = "OK"})
app$add_get("/secure/hello1", function(req, res) {res$body = "OK"})
app$add_get("/secure/hello2", function(req, res) {res$body = "OK"})
```
Request with valid token to `/secure/hello1`:
```{r}
headers = list("Authorization" = "Bearer super_secure_token_1")
req = Request$new(
path = "/secure/hello1",
headers = headers
)
res = app$process_request(req)
res$body
```
Request with invalid token to `/secure/hello2`:
```{r}
headers = list("Authorization" = "Bearer abcd")
req = Request$new(
path = "/secure/hello2",
headers = headers
)
res = app$process_request(req)
res$body
```
Request to endpoint which doesn't require authorization: `/hello0`:
```{r}
req = Request$new(path = "/hello0")
res = app$process_request(req)
res$body
```