-
Notifications
You must be signed in to change notification settings - Fork 6
/
authenticate.bbj
222 lines (165 loc) · 7.33 KB
/
authenticate.bbj
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
use com.basiscomponents.db.DataRow
use com.nimbusds.jose.JWSAlgorithm
use com.nimbusds.jose.JWSHeader
use com.nimbusds.jose.JWSSigner
use com.nimbusds.jose.JWSVerifier
use com.nimbusds.jose.crypto.MACSigner
use com.nimbusds.jose.crypto.MACVerifier
use com.nimbusds.jwt.JWTClaimsSet
use com.nimbusds.jwt.SignedJWT
use com.nimbusds.jose.JWSHeader.Builder
use com.nimbusds.jose.JOSEObjectType
rem HMAC authentication
use javax.crypto.Mac
use javax.crypto.spec.SecretKeySpec
AUTHENTICATION:
enter
rem This section covers the authentication of the user based on the
rem authentication type and the authentication token.
rem JWT Authentication
if authtype$ = "BEARER" then
jwt$ = auth$
rem TODO: adjust these things according to your application's needs:
secret$ = "your-256-bit-secret"
verifier! = new MACVerifier(secret$)
signedJWT! = SignedJWT.parse(jwt$, err=jwt_failed)
isValid = signedJWT!.verify(verifier!, err=jwt_failed)
if !isValid then
throw "Failed to validate the token's authenticity", 401
endif
claimsSet! = signedJWT!.getJWTClaimsSet()
exp! = claimsSet!.getExpirationTime()
if exp!.getTime() < System.currentTimeMillis() then
throw "Access Token Expired", 401
endif
authenticated = 1
payload$ = signedJWT!.getPayload().toString()
jwtpayload! = DataRow.fromJson(payload$)
rem setting user$ to the JWT subject string
rem if the username is required later and it's in the payload this would probably be the best place to set user$
user$ = claimsSet!.getSubject()
rem storing the payload as a DataRow into the object table
rem if you need it later, simply use something like this:
rem jwtpayload! = BBjAPI().getObjectTable().get("JWTPAYLOAD")
BBjAPI().getObjectTable().put("JWTPAYLOAD", jwtpayload!)
rem if your payload is needed in original json, we also put the payload$ into an STBL
dummy$ = STBL("JWTPAYLOAD", payload$)
exit
endif
rem HMAC Authentication
if authtype$ = "HMAC" then
hmac$ = auth$
rem request is a HashMap at this point and not the actual
request! = BBjAPI().getGlobalNamespace().getValue(ses$ + "_REQUEST", err=*next)
if request! = null() then
throw "Failed to retrieve request for the HMAC authentication", 25
endif
rem TODO: adjust these things according to your application's needs:
secret! = "your-256-bit-secret"
sha256_HMAC! = Mac.getInstance("HmacSHA256")
secret_key! = new SecretKeySpec(secret!.getBytes(), "HmacSHA256")
sha256_HMAC!.init(secret_key!)
body! = new String(request!.get("body"))
data! = new String(body!.getBytes("UTF-8"))
hash! = new String(java.util.Base64.getEncoder().encode(sha256_HMAC!.doFinal(data!)))
if hmac$ <> hash! then
throw "Unauthorized", 25
endif
authenticated = 1
exit
endif
rem BASIC Authentication
if authtype$="BASIC" then
auth$ = java.util.Base64.getDecoder().decode(auth$, err=*next)
user$ = auth$(1,pos(":"=auth$)-1)
password$ = auth$(pos(":"=auth$)+1)
rem changing the string encoding to the server's encoding
user$ = new String(user$,"utf-8").getBytes(info(1,2))
password$ = new String(password$,"utf-8").getBytes(info(1,2))
rem BASIC Authentication against the Admin API
adminAPI! = BBjAPI().getAdmin(user$, password$, err=*next)
if adminAPI! = null() then
throw "Wrong username and/or password", 25
else
authenticated = 1
adminAPI!.release()
endif
endif
exit
PRE_AUTHENTICATION:
rem The code in this section gathers the information to perform the
rem user authentication later on by evaluating the request's header / body values.
rem By default, this section checks the request's authorization header and extracts the
rem token or the username + password from it, which will be used in the authorization process
rem defined in the top of this program.
rem Additionally it can be used to restrict the access to the RestBridge's features and
rem the BCs. For example: If you don't want to expose certain BCs via the RestBridge, the
rem code in this section could refuse the requests to those BCs and throw an Error.
enter
token$ = request!.getParameter("_TOKEN")
if token$ > "" then
auth$="Basic " + token$
fi
token$ = request!.getParameter("_JTOKEN")
if token$ > "" then
auth$ = "Bearer "+token$
fi
if auth$ = "" then
auth$ = str(request!.getHeader("authorization", err=*next))
fi
if cvs(str(request!.getParameter("grant_type")), 4) = "PASSWORD" then
user$ = request!.getParameter("username")
password$ = request!.getParameter("password")
auth$="Basic "+java.util.Base64.getEncoder().encode(user$+":"+password$)
rem we're using the Basic authentication way to pass username and password to the Adapter
rem so that the "usual" login can be used to check the credentials
fi
if cvs(str(servletParams!.get("GRANT_TYPE",err=*endif)),4) = "PASSWORD" then
user$ = str(servletParams!.get("USERNAME"),err=*next)
password$ = str(servletParams!.get("PASSWORD"),err=*next)
if user$>"" and password$>"" then
auth$="Basic "+java.util.Base64.getEncoder().encode(user$+":"+password$)
System.out.println("Debug: "+auth$)
endif
endif
exit
jwt_failed:
emes$=errmes(-1)
if pos(":"=emes$)>0 then
emes$=cvs(emes$(pos(":"=emes$)+1),3)
endif
System.out.println("Invalid token: " + errmes(-1))
throw "Failed to validate the token's authenticity", 401
ON_REQUEST:
rem This label gets called when a request has been made to the RestBridge.
exit
ON_CLEANUP:
rem This label gets called right before the RestBCAdapter program ends.
rem Use it to perform any outstanding cleanup operations.
exit
ISSUE_JWT:
enter
rem TODO: adjust these things according to your application's needs:
secret$ = "your-256-bit-secret"
issuer$ = "BBj REST Bridge Plug-In"
audience$ = "You"
rem make it expire in one day
expires_in = 24*60*60*1000
exp! = new java.util.Date(System.currentTimeMillis()+expires_in)
headerBuilder! = new JWSHeader.Builder(JWSAlgorithm.HS256)
headerBuilder!.type(JOSEObjectType.JWT)
header! = headerBuilder!.build()
claimsSetBuilder! = new JWTClaimsSet.Builder()
claimsSetBuilder!.subject(user$)
claimsSetBuilder!.audience(audience$)
claimsSetBuilder!.issuer(issuer$)
claimsSetBuilder!.expirationTime(exp!)
claimsSet! = claimsSetBuilder!.build()
signer! = new MACSigner(secret$)
signedJWT! = new SignedJWT(header!, claimsSet!)
signedJWT!.sign(signer!)
token! = signedJWT!.serialize()
token_rec!.setFieldValue("access_token", token!)
token_rec!.setFieldValue("expires_in", expires_in)
token_rec!.setFieldValue("expires", exp!.toString())
exit