Macros for SAS Application Developers
https://github.com/sasjs/core
Loading...
Searching...
No Matches
mv_registerclient.sas
Go to the documentation of this file.
1/**
2 @file mv_registerclient.sas
3 @brief Register Client and Secret (admin task)
4 @details When building apps on SAS Viya, a client id and secret are usually
5 required. In order to generate them, the Consul Token is required. To access
6 this token, you need to be a system administrator (it is not enough to be in
7 the SASAdministrator group in SAS Environment Manager).
8
9 If you are registering a lot of clients / secrets, you may find it more
10 convenient to use the [Viya Token Generator]
11 (https://sasjs.io/apps/#viya-client-token-generator) (a SASjs Web App to
12 automate the generation of clients & secrets with various settings).
13
14 For further information on clients / secrets, see;
15 @li https://developer.sas.com/reference/auth/#register
16 @li https://proc-x.com/2019/01/authentication-to-sas-viya-a-couple-of-approaches
17 @li https://cli.sasjs.io/faq/#how-can-i-obtain-a-viya-client-and-secret
18
19 The default viyaroot location is: `/opt/sas/viya/config`
20
21 Usage:
22
23 %* compile macros;
24 filename mc url
25 "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
26 %inc mc;
27
28 %* generate random client details with openid scope;
29 %mv_registerclient(scopes=openid )
30
31 %* generate random client using consul token as input parameter;
32 %mv_registerclient(consul_token=12x34sa43v2345n234lasd)
33
34 %* specific client with just openid scope;
35 %mv_registerclient(client_id=YourClient
36 ,client_secret=YourSecret
37 ,scopes=openid
38 )
39
40 %* generate random client with 90/180 second access/refresh token expiry;
41 %mv_registerclient(scopes=openid
42 ,access_token_validity=90
43 ,refresh_token_validity=180
44 )
45
46 @param [in,out] client_id= The client name. Auto generated if blank.
47 @param [in,out] client_secret= Client secret. Auto generated if client is
48 blank.
49 @param [in] consul_token= (0) Provide the actual consul token value here if
50 using Viya 4 or above.
51 @param [in] scopes= (openid) List of space-seperated unquoted scopes
52 @param [in] grant_type= (authorization_code|refresh_token) Valid values are
53 "password" or "authorization_code" (unquoted). Pipe seperated.
54 @param [out] outds=(mv_registerclient) The dataset to contain the registered
55 client id and secret
56 @param [in] access_token_validity= (DEFAULT) The access token duration in
57 seconds. A value of DEFAULT will omit the entry (and use system default)
58 @param [in] refresh_token_validity= (DEFAULT) The duration of validity of the
59 refresh token in seconds. A value of DEFAULT will omit the entry (and use
60 system default)
61 @param [in] client_name= (DEFAULT) An optional, human readable name for the
62 client.
63 @param [in] required_user_groups= A list of group names. If a user does not
64 belong to all the required groups, the user will not be authenticated and no
65 tokens are issued to this client for that user. If this field is not
66 specified, authentication and token issuance proceeds normally.
67 @param [in] autoapprove= During the auth step the user can choose which scope
68 to apply. Setting this to true will autoapprove all the client scopes.
69 @param [in] use_session= If true, access tokens issued to this client will be
70 associated with an HTTP session and revoked upon logout or time-out.
71 @param [out] outjson= (_null_) A dataset containing the lines of JSON
72 submitted. Useful for debugging.
73
74 @version VIYA V.03.04
75 @author Allan Bowe, source: https://github.com/sasjs/core
76
77 <h4> SAS Macros </h4>
78 @li mf_getplatform.sas
79 @li mf_getuniquefileref.sas
80 @li mf_getuniquelibref.sas
81 @li mf_loc.sas
82 @li mf_getquotedstr.sas
83 @li mf_getuser.sas
84 @li mp_abort.sas
85
86 @sasjslint {"maxLineLength": 82}
87**/
88
89%macro mv_registerclient(client_id=
90 ,client_secret=
91 ,consul_token=0
92 ,client_name=DEFAULT
93 ,scopes=openid
94 ,grant_type=authorization_code|refresh_token
95 ,required_user_groups=
96 ,autoapprove=
97 ,use_session=
98 ,outds=mv_registerclient
99 ,access_token_validity=DEFAULT
100 ,refresh_token_validity=DEFAULT
101 ,outjson=_null_
102 );
103%local fname1 fname2 fname3 libref access_token url tokloc msg;
104
105%if client_name=DEFAULT %then %let client_name=
106 Generated by %mf_getuser() (&sysuserid) on %sysfunc(datetime(),datetime19.
107 ) using SASjs;
108
109options noquotelenmax;
110
111%if "&consul_token"="0" %then %do;
112 /* first, get consul token needed to get client id / secret */
113 %let tokloc=/etc/SASSecurityCertificateFramework/tokens/consul/default;
114 %let tokloc=%mf_loc(VIYACONFIG)&tokloc/client.token;
115
116 %if %sysfunc(fileexist(&tokloc))=0 %then %do;
117 %let msg=Unable to access the consul token at &tokloc;
118 %put &sysmacroname: &msg;
119 %put Try passing the value in the consul= macro parameter;
120 %put See docs: https://core.sasjs.io/mv__registerclient_8sas.html;
121 %mp_abort(mac=mv_registerclient,msg=%str(&msg))
122 %end;
123
124 data _null_;
125 infile "&tokloc";
126 input token:$64.;
127 call symputx('consul_token',token);
128 run;
129
130 %if "&consul_token"="0" %then %do;
131 %put &sysmacroname: Unable to source the consul token from &tokloc;
132 %put It seems your account (&sysuserid) does not have admin rights;
133 %put Please speak with your platform adminstrator;
134 %put Docs: https://core.sasjs.io/mv__registerclient_8sas.html;
135 %abort;
136 %end;
137%end;
138
139%local base_uri; /* location of rest apis */
140%let base_uri=%mf_getplatform(VIYARESTAPI);
141
142/* request the client details */
143%let fname1=%mf_getuniquefileref();
144proc http method='POST' out=&fname1
145 url="&base_uri/SASLogon/oauth/clients/consul?callback=false%str(&)%trim(
146 )serviceId=app";
147 headers "X-Consul-Token"="&consul_token";
148run;
149
150%put &=SYS_PROCHTTP_STATUS_CODE;
151%put &=SYS_PROCHTTP_STATUS_PHRASE;
152
153%let libref=%mf_getuniquelibref();
154libname &libref JSON fileref=&fname1;
155
156/* extract the token */
157data _null_;
158 set &libref..root;
159 call symputx('access_token',access_token,'l');
160run;
161
162/**
163 * register the new client
164 */
165%let fname2=%mf_getuniquefileref();
166%if x&client_id.x=xx %then %do;
167 %let client_id=client_%sysfunc(ranuni(0),hex16.);
168 %let client_secret=secret_%sysfunc(ranuni(0),hex16.);
169%end;
170
171%let scopes=%sysfunc(coalescec(&scopes,openid));
172%let scopes=%mf_getquotedstr(&scopes,QUOTE=D,indlm=|);
173%let grant_type=%mf_getquotedstr(&grant_type,QUOTE=D,indlm=|);
174%let required_user_groups=
175 %mf_getquotedstr(&required_user_groups,QUOTE=D,indlm=|);
176
177data _null_;
178 file &fname2;
179 length clientid clientsecret clientname scope grant_types reqd_groups
180 autoapprove $256.;
181 clientid='"client_id":'!!quote(trim(symget('client_id')));
182 clientsecret=',"client_secret":'!!quote(trim(symget('client_secret')));
183 clientname=',"name":'!!quote(trim(symget('client_name')));
184 scope=',"scope":['!!symget('scopes')!!']';
185 grant_types=symget('grant_type');
186 if grant_types = '""' then grant_types ='';
187 grant_types=cats(',"authorized_grant_types": [',grant_types,']');
188 reqd_groups=symget('required_user_groups');
189 if reqd_groups = '""' then reqd_groups ='';
190 else reqd_groups=cats(',"required_user_groups":[',reqd_groups,']');
191 autoapprove=trim(symget('autoapprove'));
192 if not missing(autoapprove) then autoapprove=
193 cats(',"autoapprove":',autoapprove);
194 use_session=trim(symget('use_session'));
195 if not missing(use_session) then use_session=
196 cats(',"use_session":',use_session);
197
198 put '{' clientid ;
199 put clientsecret ;
200 put clientname;
201 put scope;
202 put grant_types;
203 if not missing(reqd_groups) then put reqd_groups;
204 put autoapprove;
205 put use_session;
206%if &access_token_validity ne DEFAULT %then %do;
207 put ',"access_token_validity":' "&access_token_validity";
208%end;
209%if &refresh_token_validity ne DEFAULT %then %do;
210 put ',"refresh_token_validity":' "&refresh_token_validity";
211%end;
212
213 put ',"redirect_uri": "urn:ietf:wg:oauth:2.0:oob"';
214 put '}';
215run;
216
217%let fname3=%mf_getuniquefileref();
218proc http method='POST' in=&fname2 out=&fname3
219 url="&base_uri/SASLogon/oauth/clients";
220 headers "Content-Type"="application/json"
221 "Authorization"="Bearer &access_token";
222run;
223
224/* show response */
225%local err;
226%let err=NONE;
227data _null_;
228 infile &fname3;
229 input;
230 if _infile_=:'{"err'!!'or":' then do;
231 length message $32767;
232 message=scan(_infile_,-2,'"');
233 call symputx('err',message,'l');
234 end;
235run;
236%if "&err" ne "NONE" %then %do;
237 %put %str(ERR)OR: &err;
238%end;
239
240/* prepare url */
241%if %index(%superq(grant_type),authorization_code) %then %do;
242 data _null_;
243 if symexist('_baseurl') then do;
244 url=symget('_baseurl');
245 if subpad(url,length(url)-9,9)='SASStudio'
246 then url=substr(url,1,length(url)-11);
247 else url="&systcpiphostname";
248 end;
249 else url="&systcpiphostname";
250 call symputx('url',url);
251 run;
252%end;
253
254%put Please provide the following details to the developer:;
255%put ;
256%put CLIENT_ID=&client_id;
257%put CLIENT_SECRET=&client_secret;
258%put GRANT_TYPE=&grant_type;
259%put;
260%if %index(%superq(grant_type),authorization_code) %then %do;
261 /* cannot use base_uri here as it includes the protocol which may be incorrect
262 externally */
263 %put NOTE: Visit the link below and select 'openid' to get the grant code:;
264 %put NOTE- ;
265 %put NOTE- &url/SASLogon/oauth/authorize?client_id=&client_id%str(&)%trim(
266 )response_type=code;
267 %put NOTE- ;
268%end;
269
270data &outds;
271 client_id=symget('client_id');
272 client_secret=symget('client_secret');
273 error=symget('err');
274run;
275
276data &outjson;
277 infile &fname2;
278 input;
279 line=_infile_;
280run;
281
282/* clear refs */
283filename &fname1 clear;
284filename &fname2 clear;
285filename &fname3 clear;
286libname &libref clear;
287
288%mend mv_registerclient;