github.com/greenpau/go-authcrunch@v1.1.4/pkg/idp/oauth/config_test.go (about) 1 // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package oauth 16 17 import ( 18 "fmt" 19 "github.com/greenpau/go-authcrunch/internal/tests" 20 "github.com/greenpau/go-authcrunch/pkg/authn/icons" 21 "github.com/greenpau/go-authcrunch/pkg/errors" 22 "testing" 23 ) 24 25 func TestValidateConfig(t *testing.T) { 26 testcases := []struct { 27 name string 28 config *Config 29 want *Config 30 shouldErr bool 31 err error 32 }{ 33 { 34 name: "validate generic oauth config", 35 config: &Config{ 36 Name: "contoso", 37 Realm: "contoso", 38 Driver: "generic", 39 ClientID: "foo", 40 ClientSecret: "bar", 41 BaseAuthURL: "https://localhost/oauth", 42 MetadataURL: "https://localhost/oauth/.well-known/openid-configuration", 43 }, 44 want: &Config{ 45 Name: "contoso", 46 Realm: "contoso", 47 Driver: "generic", 48 ClientID: "foo", 49 ClientSecret: "bar", 50 BaseAuthURL: "https://localhost/oauth", 51 // After the validation. 52 ServerName: "localhost", 53 IdentityTokenName: "id_token", 54 Scopes: []string{"openid", "email", "profile"}, 55 ResponseType: []string{"code"}, 56 RequiredTokenFields: []string{"access_token", "id_token"}, 57 MetadataURL: "https://localhost/oauth/.well-known/openid-configuration", 58 LoginIcon: &icons.LoginIcon{ 59 ClassName: "lab la-codepen la-2x", 60 Color: "white", 61 BackgroundColor: "#324960", 62 TextColor: "#37474f", 63 }, 64 }, 65 }, 66 { 67 name: "validate facebook oauth config", 68 config: &Config{ 69 Name: "facebook", 70 Realm: "facebook", 71 Driver: "facebook", 72 ClientID: "foo", 73 ClientSecret: "bar", 74 }, 75 want: &Config{ 76 Name: "facebook", 77 Realm: "facebook", 78 Driver: "facebook", 79 ClientID: "foo", 80 ClientSecret: "bar", 81 // After the validation. 82 ServerName: "www.facebook.com", 83 IdentityTokenName: "id_token", 84 Scopes: []string{"email"}, 85 BaseAuthURL: "https://www.facebook.com/v12.0/dialog/", 86 ResponseType: []string{"code"}, 87 RequiredTokenFields: []string{"access_token"}, 88 AuthorizationURL: "https://www.facebook.com/v12.0/dialog/oauth", 89 TokenURL: "https://graph.facebook.com/v12.0/oauth/access_token", 90 LoginIcon: &icons.LoginIcon{ 91 ClassName: "lab la-facebook la-2x", 92 Color: "white", 93 BackgroundColor: "#0d47a1", 94 Text: "Facebook", 95 TextColor: "#37474f", 96 }, 97 }, 98 }, 99 { 100 name: "validate discord oauth config", 101 config: &Config{ 102 Name: "discord", 103 Realm: "discord", 104 Driver: "discord", 105 ClientID: "foo", 106 ClientSecret: "bar", 107 }, 108 want: &Config{ 109 Name: "discord", 110 Realm: "discord", 111 Driver: "discord", 112 ClientID: "foo", 113 ClientSecret: "bar", 114 // After the validation. 115 ServerName: "discord.com", 116 IdentityTokenName: "id_token", // maybe change this to access_token 117 Scopes: []string{"identify"}, 118 BaseAuthURL: "https://discord.com/oauth2", 119 ResponseType: []string{"code"}, 120 RequiredTokenFields: []string{"access_token"}, 121 AuthorizationURL: "https://discord.com/oauth2/authorize", 122 TokenURL: "https://discord.com/api/oauth2/token", 123 LoginIcon: &icons.LoginIcon{ 124 ClassName: "lab la-discord la-2x", 125 Color: "white", 126 Text: "Discord", 127 BackgroundColor: "#5865f2", 128 TextColor: "#37474f", 129 }, 130 }, 131 }, 132 { 133 name: "validate nextcloud oauth config", 134 config: &Config{ 135 Name: "nextcloud", 136 Realm: "nextcloud", 137 Driver: "nextcloud", 138 ClientID: "foo", 139 ClientSecret: "bar", 140 BaseAuthURL: "https://localhost/oauth", 141 }, 142 want: &Config{ 143 Name: "nextcloud", 144 Realm: "nextcloud", 145 Driver: "nextcloud", 146 ClientID: "foo", 147 ClientSecret: "bar", 148 BaseAuthURL: "https://localhost/oauth", 149 // After the validation. 150 ServerName: "localhost", 151 IdentityTokenName: "id_token", 152 Scopes: []string{"email"}, 153 ResponseType: []string{"code"}, 154 RequiredTokenFields: []string{"access_token", "id_token"}, 155 AuthorizationURL: "https://localhost/oauth/apps/oauth2/authorize", 156 TokenURL: "https://localhost/oauth/apps/oauth2/api/v1/token", 157 LoginIcon: &icons.LoginIcon{ 158 ClassName: "lab la-codepen la-2x", 159 Color: "white", 160 BackgroundColor: "#324960", 161 TextColor: "#37474f", 162 }, 163 }, 164 }, 165 { 166 name: "validate okta oauth config", 167 config: &Config{ 168 Name: "okta", 169 Realm: "okta", 170 Driver: "okta", 171 ClientID: "foo", 172 ClientSecret: "bar", 173 DomainName: "foo.okta.dev", 174 ServerID: "default", 175 Scopes: []string{ 176 "openid", "email", "profile", "groups", 177 }, 178 }, 179 want: &Config{ 180 Name: "okta", 181 Realm: "okta", 182 Driver: "okta", 183 ClientID: "foo", 184 ClientSecret: "bar", 185 DomainName: "foo.okta.dev", 186 ServerID: "default", 187 Scopes: []string{ 188 "openid", "email", "profile", "groups", 189 }, 190 // After the validation. 191 ServerName: "foo.okta.dev", 192 IdentityTokenName: "id_token", 193 BaseAuthURL: "https://foo.okta.dev/oauth2/default/", 194 MetadataURL: "https://foo.okta.dev/oauth2/default/.well-known/openid-configuration?client_id=foo", 195 ResponseType: []string{"code"}, 196 RequiredTokenFields: []string{"access_token", "id_token"}, 197 LoginIcon: &icons.LoginIcon{ 198 ClassName: "lab la-codepen la-2x", 199 Color: "white", 200 BackgroundColor: "#324960", 201 TextColor: "#37474f", 202 }, 203 }, 204 }, 205 { 206 name: "validate google oauth config", 207 config: &Config{ 208 Name: "google", 209 Realm: "google", 210 Driver: "google", 211 ClientID: "foo", 212 ClientSecret: "bar", 213 }, 214 want: &Config{ 215 Name: "google", 216 Realm: "google", 217 Driver: "google", 218 // ClientID: "foo", 219 ClientSecret: "bar", 220 // After the validation. 221 ClientID: "foo.apps.googleusercontent.com", 222 ServerName: "accounts.google.com", 223 IdentityTokenName: "id_token", 224 Scopes: []string{"openid", "email", "profile"}, 225 BaseAuthURL: "https://accounts.google.com/o/oauth2/v2/", 226 MetadataURL: "https://accounts.google.com/.well-known/openid-configuration", 227 ResponseType: []string{"code"}, 228 RequiredTokenFields: []string{"access_token", "id_token"}, 229 LoginIcon: &icons.LoginIcon{ 230 ClassName: "lab la-google la-2x", 231 Color: "white", 232 BackgroundColor: "#e53935", 233 Text: "Google", 234 TextColor: "#37474f", 235 }, 236 }, 237 }, 238 { 239 name: "validate github oauth config", 240 config: &Config{ 241 Name: "github", 242 Realm: "github", 243 Driver: "github", 244 ClientID: "foo", 245 ClientSecret: "bar", 246 }, 247 want: &Config{ 248 Name: "github", 249 Realm: "github", 250 Driver: "github", 251 ClientID: "foo", 252 ClientSecret: "bar", 253 // After the validation. 254 ServerName: "github.com", 255 IdentityTokenName: "id_token", 256 // Scopes: []string{"openid", "email", "profile"}, 257 Scopes: []string{"read:user"}, 258 BaseAuthURL: "https://github.com/login/oauth/", 259 ResponseType: []string{"code"}, 260 RequiredTokenFields: []string{"access_token"}, 261 AuthorizationURL: "https://github.com/login/oauth/authorize", 262 TokenURL: "https://github.com/login/oauth/access_token", 263 LoginIcon: &icons.LoginIcon{ 264 ClassName: "lab la-github la-2x", 265 Color: "#f6f8fa", 266 BackgroundColor: "#24292f", 267 Text: "Github", 268 TextColor: "#37474f", 269 }, 270 }, 271 }, 272 { 273 name: "validate gitlab oauth config", 274 config: &Config{ 275 Name: "gitlab", 276 Realm: "gitlab", 277 Driver: "gitlab", 278 ClientID: "foo", 279 ClientSecret: "bar", 280 Scopes: []string{ 281 "openid", "email", "profile", 282 }, 283 UserGroupFilters: []string{ 284 "barfoo", "^a", 285 }, 286 }, 287 want: &Config{ 288 Name: "gitlab", 289 Realm: "gitlab", 290 Driver: "gitlab", 291 ClientID: "foo", 292 ClientSecret: "bar", 293 Scopes: []string{ 294 "openid", "email", "profile", 295 }, 296 UserGroupFilters: []string{ 297 "barfoo", "^a", 298 }, 299 // After the validation. 300 DomainName: "gitlab.com", 301 ServerName: "gitlab.com", 302 IdentityTokenName: "id_token", 303 BaseAuthURL: "https://gitlab.com/", 304 MetadataURL: "https://gitlab.com/.well-known/openid-configuration", 305 ResponseType: []string{"code"}, 306 RequiredTokenFields: []string{"access_token", "id_token"}, 307 LoginIcon: &icons.LoginIcon{ 308 ClassName: "lab la-gitlab la-2x", 309 Color: "white", 310 BackgroundColor: "#fc6d26", 311 TextColor: "#37474f", 312 }, 313 }, 314 }, 315 { 316 name: "validate azure oauth config", 317 config: &Config{ 318 Name: "azure", 319 Realm: "azure", 320 Driver: "azure", 321 ClientID: "foo", 322 ClientSecret: "bar", 323 }, 324 want: &Config{ 325 Name: "azure", 326 Realm: "azure", 327 Driver: "azure", 328 ClientID: "foo", 329 ClientSecret: "bar", 330 // After the validation. 331 ServerName: "login.microsoftonline.com", 332 TenantID: "common", 333 IdentityTokenName: "id_token", 334 Scopes: []string{"openid", "email", "profile"}, 335 BaseAuthURL: "https://login.microsoftonline.com/common/oauth2/v2.0/", 336 MetadataURL: "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration", 337 ResponseType: []string{"code"}, 338 RequiredTokenFields: []string{"access_token", "id_token"}, 339 LoginIcon: &icons.LoginIcon{ 340 ClassName: "lab la-windows la-2x", 341 Color: "white", 342 BackgroundColor: "#03a9f4", 343 Text: "Azure", 344 TextColor: "#37474f", 345 }, 346 }, 347 }, 348 { 349 name: "validate gitlab oauth config with custom domain name", 350 config: &Config{ 351 Name: "gitlab", 352 Realm: "gitlab", 353 Driver: "gitlab", 354 ClientID: "foo", 355 ClientSecret: "bar", 356 DomainName: "gitlab.contoso.com", 357 Scopes: []string{ 358 "openid", "email", "profile", 359 }, 360 UserGroupFilters: []string{ 361 "barfoo", "^a", 362 }, 363 }, 364 want: &Config{ 365 Name: "gitlab", 366 Realm: "gitlab", 367 Driver: "gitlab", 368 ClientID: "foo", 369 ClientSecret: "bar", 370 DomainName: "gitlab.contoso.com", 371 Scopes: []string{ 372 "openid", "email", "profile", 373 }, 374 UserGroupFilters: []string{ 375 "barfoo", "^a", 376 }, 377 // After the validation. 378 ServerName: "gitlab.contoso.com", 379 IdentityTokenName: "id_token", 380 BaseAuthURL: "https://gitlab.contoso.com/", 381 MetadataURL: "https://gitlab.contoso.com/.well-known/openid-configuration", 382 ResponseType: []string{"code"}, 383 RequiredTokenFields: []string{"access_token", "id_token"}, 384 LoginIcon: &icons.LoginIcon{ 385 ClassName: "lab la-gitlab la-2x", 386 Color: "white", 387 BackgroundColor: "#fc6d26", 388 TextColor: "#37474f", 389 }, 390 }, 391 }, 392 { 393 name: "test okta oauth config without server id", 394 config: &Config{ 395 Name: "okta", 396 Realm: "okta", 397 Driver: "okta", 398 ClientID: "foo", 399 ClientSecret: "bar", 400 }, 401 shouldErr: true, 402 err: errors.ErrIdentityProviderConfig.WithArgs("server id not found"), 403 }, 404 { 405 name: "test okta oauth config without domain name", 406 config: &Config{ 407 Name: "okta", 408 Realm: "okta", 409 Driver: "okta", 410 ClientID: "foo", 411 ClientSecret: "bar", 412 ServerID: "default", 413 }, 414 shouldErr: true, 415 err: errors.ErrIdentityProviderConfig.WithArgs("domain name not found"), 416 }, 417 { 418 name: "test empty config name", 419 config: &Config{ 420 Realm: "contoso", 421 }, 422 shouldErr: true, 423 err: errors.ErrIdentityProviderConfigureNameEmpty, 424 }, 425 { 426 name: "test empty config realm", 427 config: &Config{ 428 Name: "contoso", 429 }, 430 shouldErr: true, 431 err: errors.ErrIdentityProviderConfigureRealmEmpty, 432 }, 433 { 434 name: "test empty client id", 435 config: &Config{ 436 Name: "contoso", 437 Realm: "contoso", 438 Driver: "generic", 439 // ClientID: "foo", 440 // ClientSecret: "bar", 441 // BaseAuthURL: "https://localhost/oauth/", 442 }, 443 shouldErr: true, 444 err: errors.ErrIdentityProviderConfig.WithArgs("client id not found"), 445 }, 446 { 447 name: "test empty client secret", 448 config: &Config{ 449 Name: "contoso", 450 Realm: "contoso", 451 Driver: "generic", 452 ClientID: "foo", 453 // ClientSecret: "bar", 454 // BaseAuthURL: "https://localhost/oauth/", 455 }, 456 shouldErr: true, 457 err: errors.ErrIdentityProviderConfig.WithArgs("client secret not found"), 458 }, 459 { 460 name: "test unsupported identity token name", 461 config: &Config{ 462 Name: "contoso", 463 Realm: "contoso", 464 Driver: "generic", 465 ClientID: "foo", 466 ClientSecret: "bar", 467 // BaseAuthURL: "https://localhost/oauth/", 468 IdentityTokenName: "foobar", 469 }, 470 shouldErr: true, 471 err: errors.ErrIdentityProviderConfig.WithArgs( 472 fmt.Errorf("identity token name %q is unsupported", "foobar"), 473 ), 474 }, 475 { 476 name: "test empty base auth url", 477 config: &Config{ 478 Name: "contoso", 479 Realm: "contoso", 480 Driver: "generic", 481 ClientID: "foo", 482 ClientSecret: "bar", 483 // BaseAuthURL: "https://localhost/oauth/", 484 }, 485 shouldErr: true, 486 err: errors.ErrIdentityProviderConfig.WithArgs("base authentication url not found"), 487 }, 488 { 489 name: "test invalid base auth url", 490 config: &Config{ 491 Name: "contoso", 492 Realm: "contoso", 493 Driver: "generic", 494 ClientID: "foo", 495 ClientSecret: "bar", 496 BaseAuthURL: "http://^localhost", 497 MetadataURL: "https://localhost/oauth/.well-known/openid-configuration", 498 }, 499 shouldErr: true, 500 err: errors.ErrIdentityProviderConfig.WithArgs( 501 fmt.Errorf( 502 "failed to parse base auth url %q: %v", 503 "http://^localhost", 504 `parse "http://^localhost": invalid character "^" in host name`, 505 ), 506 ), 507 }, 508 { 509 name: "test invalid user group regex", 510 config: &Config{ 511 Name: "contoso", 512 Realm: "contoso", 513 Driver: "generic", 514 ClientID: "foo", 515 ClientSecret: "bar", 516 BaseAuthURL: "https://localhost/oauth", 517 MetadataURL: "https://localhost/oauth/.well-known/openid-configuration", 518 UserGroupFilters: []string{ 519 "foo(", 520 }, 521 }, 522 shouldErr: true, 523 err: errors.ErrIdentityProviderConfig.WithArgs( 524 fmt.Errorf( 525 "invalid user group pattern %q: %v", 526 "foo(", 527 "error parsing regexp: missing closing ): `foo(`", 528 ), 529 ), 530 }, 531 { 532 name: "test invalid user org regex", 533 config: &Config{ 534 Name: "contoso", 535 Realm: "contoso", 536 Driver: "generic", 537 ClientID: "foo", 538 ClientSecret: "bar", 539 BaseAuthURL: "https://localhost/oauth", 540 MetadataURL: "https://localhost/oauth/.well-known/openid-configuration", 541 UserOrgFilters: []string{ 542 "foo(", 543 }, 544 }, 545 shouldErr: true, 546 err: errors.ErrIdentityProviderConfig.WithArgs( 547 fmt.Errorf( 548 "invalid user org pattern %q: %v", 549 "foo(", 550 "error parsing regexp: missing closing ): `foo(`", 551 ), 552 ), 553 }, 554 { 555 name: "test unsupported driver name", 556 config: &Config{ 557 Name: "contoso", 558 Realm: "contoso", 559 Driver: "foobar", 560 ClientID: "foo", 561 ClientSecret: "bar", 562 BaseAuthURL: "https://localhost/oauth", 563 MetadataURL: "https://localhost/oauth/.well-known/openid-configuration", 564 }, 565 shouldErr: true, 566 err: errors.ErrIdentityProviderConfig.WithArgs( 567 fmt.Errorf("driver %q is unsupported", "foobar"), 568 ), 569 }, 570 { 571 name: "test empty driver name", 572 config: &Config{ 573 Name: "contoso", 574 Realm: "contoso", 575 // Driver: "foobar", 576 ClientID: "foo", 577 ClientSecret: "bar", 578 BaseAuthURL: "https://localhost/oauth/", 579 }, 580 shouldErr: true, 581 err: errors.ErrIdentityProviderConfig.WithArgs("driver name not found"), 582 }, 583 { 584 name: "test delayed start", 585 config: &Config{ 586 Name: "contoso", 587 Realm: "contoso", 588 Driver: "generic", 589 ClientID: "foo", 590 ClientSecret: "bar", 591 BaseAuthURL: "https://localhost/oauth", 592 MetadataURL: "https://localhost/oauth/.well-known/openid-configuration", 593 DelayStart: 10, 594 }, 595 want: &Config{ 596 Name: "contoso", 597 Realm: "contoso", 598 Driver: "generic", 599 ClientID: "foo", 600 ClientSecret: "bar", 601 BaseAuthURL: "https://localhost/oauth", 602 MetadataURL: "https://localhost/oauth/.well-known/openid-configuration", 603 DelayStart: 10, 604 // After the validation. 605 ServerName: "localhost", 606 IdentityTokenName: "id_token", 607 Scopes: []string{"openid", "email", "profile"}, 608 RetryAttempts: 2, 609 RetryInterval: 10, 610 ResponseType: []string{"code"}, 611 RequiredTokenFields: []string{"access_token", "id_token"}, 612 LoginIcon: &icons.LoginIcon{ 613 ClassName: "lab la-codepen la-2x", 614 Color: "white", 615 BackgroundColor: "#324960", 616 TextColor: "#37474f", 617 }, 618 }, 619 }, 620 { 621 name: "test custom retry attempts", 622 config: &Config{ 623 Name: "contoso", 624 Realm: "contoso", 625 Driver: "generic", 626 ClientID: "foo", 627 ClientSecret: "bar", 628 BaseAuthURL: "https://localhost/oauth", 629 MetadataURL: "https://localhost/oauth/.well-known/openid-configuration", 630 RetryAttempts: 10, 631 }, 632 want: &Config{ 633 Name: "contoso", 634 Realm: "contoso", 635 Driver: "generic", 636 ClientID: "foo", 637 ClientSecret: "bar", 638 BaseAuthURL: "https://localhost/oauth", 639 MetadataURL: "https://localhost/oauth/.well-known/openid-configuration", 640 RetryAttempts: 10, 641 // After the validation. 642 ServerName: "localhost", 643 IdentityTokenName: "id_token", 644 Scopes: []string{"openid", "email", "profile"}, 645 RetryInterval: 5, 646 ResponseType: []string{"code"}, 647 RequiredTokenFields: []string{"access_token", "id_token"}, 648 LoginIcon: &icons.LoginIcon{ 649 ClassName: "lab la-codepen la-2x", 650 Color: "white", 651 BackgroundColor: "#324960", 652 TextColor: "#37474f", 653 }, 654 }, 655 }, 656 { 657 name: "test config with predefined keys", 658 config: &Config{ 659 Name: "contoso", 660 Realm: "contoso", 661 Driver: "generic", 662 ClientID: "foo", 663 ClientSecret: "bar", 664 BaseAuthURL: "https://localhost/oauth", 665 ResponseType: []string{"code"}, 666 RequiredTokenFields: []string{"access_token"}, 667 AuthorizationURL: "https://localhost/oauth/authorize", 668 TokenURL: "https://localhost/oauth/access_token", 669 JwksKeys: map[string]string{ 670 "87329db33bf": "../../../testdata/oauth/87329db33bf_pub.pem", 671 }, 672 }, 673 want: &Config{ 674 Name: "contoso", 675 Realm: "contoso", 676 Driver: "generic", 677 ClientID: "foo", 678 ClientSecret: "bar", 679 BaseAuthURL: "https://localhost/oauth", 680 ResponseType: []string{"code"}, 681 RequiredTokenFields: []string{"access_token"}, 682 AuthorizationURL: "https://localhost/oauth/authorize", 683 TokenURL: "https://localhost/oauth/access_token", 684 JwksKeys: map[string]string{ 685 "87329db33bf": "../../../testdata/oauth/87329db33bf_pub.pem", 686 }, 687 688 // After the validation. 689 ServerName: "localhost", 690 IdentityTokenName: "id_token", 691 Scopes: []string{"openid", "email", "profile"}, 692 LoginIcon: &icons.LoginIcon{ 693 ClassName: "lab la-codepen la-2x", 694 Color: "white", 695 BackgroundColor: "#324960", 696 TextColor: "#37474f", 697 }, 698 }, 699 }, 700 } 701 for _, tc := range testcases { 702 t.Run(tc.name, func(t *testing.T) { 703 msgs := []string{fmt.Sprintf("test name: %s", tc.name)} 704 705 err := tc.config.Validate() 706 707 if tests.EvalErrWithLog(t, err, "Config.Validate", tc.shouldErr, tc.err, msgs) { 708 return 709 } 710 711 tests.EvalObjectsWithLog(t, "Config.Content", tc.want, tc.config, msgs) 712 }) 713 } 714 }