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**/
87
88%macro mv_registerclient(client_id=
89 ,client_secret=
90 ,consul_token=0
91 ,client_name=DEFAULT
92 ,scopes=openid
93 ,grant_type=authorization_code|refresh_token
94 ,required_user_groups=
95 ,autoapprove=
96 ,use_session=
97 ,outds=mv_registerclient
98 ,access_token_validity=DEFAULT
99 ,refresh_token_validity=DEFAULT
100 ,outjson=_null_
101 );
102%local fname1 fname2 fname3 libref access_token url tokloc msg;
103
104%if client_name=DEFAULT %then %let client_name=
105 Generated by %mf_getuser() (&sysuserid) on %sysfunc(datetime(),datetime19.
106 ) using SASjs;
107
108options noquotelenmax;
109
110%if "&consul_token"="0" %then %do;
111 /* first, get consul token needed to get client id / secret */
112 %let tokloc=/etc/SASSecurityCertificateFramework/tokens/consul/default;
113 %let tokloc=%mf_loc(VIYACONFIG)&tokloc/client.token;
114
115 %if %sysfunc(fileexist(&tokloc))=0 %then %do;
116 %let msg=Unable to access the consul token at &tokloc;
117 %put &sysmacroname: &msg;
118 %put Try passing the value in the consul= macro parameter;
119 %put See docs: https://core.sasjs.io/mv__registerclient_8sas.html;
120 %mp_abort(mac=mv_registerclient,msg=%str(&msg))
121 %end;
122
123 data _null_;
124 infile "&tokloc";
125 input token:$64.;
126 call symputx('consul_token',token);
127 run;
128
129 %if "&consul_token"="0" %then %do;
130 %put &sysmacroname: Unable to source the consul token from &tokloc;
131 %put It seems your account (&sysuserid) does not have admin rights;
132 %put Please speak with your platform adminstrator;
133 %put Docs: https://core.sasjs.io/mv__registerclient_8sas.html;
134 %abort;
135 %end;
136%end;
137
138%local base_uri; /* location of rest apis */
139%let base_uri=%mf_getplatform(VIYARESTAPI);
140
141/* request the client details */
142%let fname1=%mf_getuniquefileref();
143proc http method='POST' out=&fname1
144 url="&base_uri/SASLogon/oauth/clients/consul?callback=false%str(&)%trim(
145 )serviceId=app";
146 headers "X-Consul-Token"="&consul_token";
147run;
148
149%put &=SYS_PROCHTTP_STATUS_CODE;
150%put &=SYS_PROCHTTP_STATUS_PHRASE;
151
152%let libref=%mf_getuniquelibref();
153libname &libref JSON fileref=&fname1;
154
155/* extract the token */
156data _null_;
157 set &libref..root;
158 call symputx('access_token',access_token,'l');
159run;
160
161/**
162 * register the new client
163 */
164%let fname2=%mf_getuniquefileref();
165%if x&client_id.x=xx %then %do;
166 %let client_id=client_%sysfunc(ranuni(0),hex16.);
167 %let client_secret=secret_%sysfunc(ranuni(0),hex16.);
168%end;
169
170%let scopes=%sysfunc(coalescec(&scopes,openid));
171%let scopes=%mf_getquotedstr(&scopes,QUOTE=D,indlm=|);
172%let grant_type=%mf_getquotedstr(&grant_type,QUOTE=D,indlm=|);
173%let required_user_groups=
174 %mf_getquotedstr(&required_user_groups,QUOTE=D,indlm=|);
175
176data _null_;
177 file &fname2;
178 length clientid clientsecret clientname scope grant_types reqd_groups
179 autoapprove $256.;
180 clientid='"client_id":'!!quote(trim(symget('client_id')));
181 clientsecret=',"client_secret":'!!quote(trim(symget('client_secret')));
182 clientname=',"name":'!!quote(trim(symget('client_name')));
183 scope=',"scope":['!!symget('scopes')!!']';
184 grant_types=symget('grant_type');
185 if grant_types = '""' then grant_types ='';
186 grant_types=cats(',"authorized_grant_types": [',grant_types,']');
187 reqd_groups=symget('required_user_groups');
188 if reqd_groups = '""' then reqd_groups ='';
189 else reqd_groups=cats(',"required_user_groups":[',reqd_groups,']');
190 autoapprove=trim(symget('autoapprove'));
191 if not missing(autoapprove) then autoapprove=
192 cats(',"autoapprove":',autoapprove);
193 use_session=trim(symget('use_session'));
194 if not missing(use_session) then use_session=
195 cats(',"use_session":',use_session);
196
197 put '{' clientid ;
198 put clientsecret ;
199 put clientname;
200 put scope;
201 put grant_types;
202 if not missing(reqd_groups) then put reqd_groups;
203 put autoapprove;
204 put use_session;
205%if &access_token_validity ne DEFAULT %then %do;
206 put ',"access_token_validity":' "&access_token_validity";
207%end;
208%if &refresh_token_validity ne DEFAULT %then %do;
209 put ',"refresh_token_validity":' "&refresh_token_validity";
210%end;
211
212 put ',"redirect_uri": "urn:ietf:wg:oauth:2.0:oob"';
213 put '}';
214run;
215
216%let fname3=%mf_getuniquefileref();
217proc http method='POST' in=&fname2 out=&fname3
218 url="&base_uri/SASLogon/oauth/clients";
219 headers "Content-Type"="application/json"
220 "Authorization"="Bearer &access_token";
221run;
222
223/* show response */
224%local err;
225%let err=NONE;
226data _null_;
227 infile &fname3;
228 input;
229 if _infile_=:'{"err'!!'or":' then do;
230 length message $32767;
231 message=scan(_infile_,-2,'"');
232 call symputx('err',message,'l');
233 end;
234run;
235%if "&err" ne "NONE" %then %do;
236 %put %str(ERR)OR: &err;
237%end;
238
239/* prepare url */
240%if %index(%superq(grant_type),authorization_code) %then %do;
241 data _null_;
242 if symexist('_baseurl') then do;
243 url=symget('_baseurl');
244 if subpad(url,length(url)-9,9)='SASStudio'
245 then url=substr(url,1,length(url)-11);
246 else url="&systcpiphostname";
247 end;
248 else url="&systcpiphostname";
249 call symputx('url',url);
250 run;
251%end;
252
253%put Please provide the following details to the developer:;
254%put ;
255%put CLIENT_ID=&client_id;
256%put CLIENT_SECRET=&client_secret;
257%put GRANT_TYPE=&grant_type;
258%put;
259%if %index(%superq(grant_type),authorization_code) %then %do;
260 /* cannot use base_uri here as it includes the protocol which may be incorrect
261 externally */
262 %put NOTE: Visit the link below and select 'openid' to get the grant code:;
263 %put NOTE- ;
264 %put NOTE- &url/SASLogon/oauth/authorize?client_id=&client_id%str(&)%trim(
265 )response_type=code;
266 %put NOTE- ;
267%end;
268
269data &outds;
270 client_id=symget('client_id');
271 client_secret=symget('client_secret');
272 error=symget('err');
273run;
274
275data &outjson;
276 infile &fname2;
277 input;
278 line=_infile_;
279run;
280
281/* clear refs */
282filename &fname1 clear;
283filename &fname2 clear;
284filename &fname3 clear;
285libname &libref clear;
286
287%mend mv_registerclient;