├── .gitignore ├── .hound.yml ├── .rspec ├── .rubocop.yml ├── .ruby-gemset ├── .ruby-version ├── .travis.yml ├── Gemfile ├── Guardfile ├── README.md ├── Rakefile ├── lib ├── omniauth-canvas.rb ├── omniauth-canvas │ └── version.rb └── omniauth │ └── strategies │ └── canvas.rb ├── omniauth-canvas.gemspec └── spec ├── omniauth └── strategies │ └── canvas_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | *.swp 19 | -------------------------------------------------------------------------------- /.hound.yml: -------------------------------------------------------------------------------- 1 | # reference: https://houndci.com/configuration 2 | 3 | fail_on_violations: true 4 | 5 | ruby: 6 | enabled: true 7 | config_file: .rubocop.yml 8 | 9 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | --format=progress 3 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Exclude: 3 | - db/schema.rb 4 | 5 | Style/AccessorMethodName: 6 | Description: Check the naming of accessor methods for get_/set_. 7 | Enabled: false 8 | 9 | Style/Alias: 10 | Description: 'Use alias_method instead of alias.' 11 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#alias-method' 12 | Enabled: false 13 | 14 | Style/ArrayJoin: 15 | Description: 'Use Array#join instead of Array#*.' 16 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#array-join' 17 | Enabled: false 18 | 19 | Style/AsciiComments: 20 | Description: 'Use only ascii symbols in comments.' 21 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-comments' 22 | Enabled: false 23 | 24 | Style/AsciiIdentifiers: 25 | Description: 'Use only ascii symbols in identifiers.' 26 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-identifiers' 27 | Enabled: false 28 | 29 | Style/Attr: 30 | Description: 'Checks for uses of Module#attr.' 31 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr' 32 | Enabled: false 33 | 34 | Metrics/BlockNesting: 35 | Description: 'Avoid excessive block nesting' 36 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count' 37 | Enabled: false 38 | 39 | Style/CaseEquality: 40 | Description: 'Avoid explicit use of the case equality operator(===).' 41 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-case-equality' 42 | Enabled: false 43 | 44 | Style/CharacterLiteral: 45 | Description: 'Checks for uses of character literals.' 46 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-character-literals' 47 | Enabled: false 48 | 49 | Style/ClassAndModuleChildren: 50 | Description: 'Checks style of children classes and modules.' 51 | Enabled: true 52 | EnforcedStyle: nested 53 | 54 | Metrics/ClassLength: 55 | Description: 'Avoid classes longer than 100 lines of code.' 56 | Enabled: false 57 | 58 | Metrics/ModuleLength: 59 | Description: 'Avoid modules longer than 100 lines of code.' 60 | Enabled: false 61 | 62 | Metrics/BlockLength: 63 | Exclude: 64 | - "**/*_spec.rb" 65 | 66 | Style/ClassVars: 67 | Description: 'Avoid the use of class variables.' 68 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-class-vars' 69 | Enabled: false 70 | 71 | Style/CollectionMethods: 72 | Enabled: true 73 | PreferredMethods: 74 | find: detect 75 | inject: reduce 76 | collect: map 77 | find_all: select 78 | 79 | Style/ColonMethodCall: 80 | Description: 'Do not use :: for method call.' 81 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#double-colons' 82 | Enabled: false 83 | 84 | Style/CommentAnnotation: 85 | Description: >- 86 | Checks formatting of special comments 87 | (TODO, FIXME, OPTIMIZE, HACK, REVIEW). 88 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#annotate-keywords' 89 | Enabled: false 90 | 91 | Metrics/AbcSize: 92 | Description: >- 93 | A calculated magnitude based on number of assignments, 94 | branches, and conditions. 95 | Enabled: false 96 | 97 | Metrics/CyclomaticComplexity: 98 | Description: >- 99 | A complexity metric that is strongly correlated to the number 100 | of test cases needed to validate a method. 101 | Enabled: false 102 | 103 | Rails/Delegate: 104 | Description: 'Prefer delegate method for delegations.' 105 | Enabled: false 106 | 107 | Style/DeprecatedHashMethods: 108 | Description: 'Checks for use of deprecated Hash methods.' 109 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-key' 110 | Enabled: false 111 | 112 | Style/Documentation: 113 | Description: 'Document classes and non-namespace modules.' 114 | Enabled: false 115 | 116 | Style/DotPosition: 117 | Description: 'Checks the position of the dot in multi-line method calls.' 118 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains' 119 | EnforcedStyle: trailing 120 | 121 | Style/DoubleNegation: 122 | Description: 'Checks for uses of double negation (!!).' 123 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-bang-bang' 124 | Enabled: false 125 | 126 | Style/EachWithObject: 127 | Description: 'Prefer `each_with_object` over `inject` or `reduce`.' 128 | Enabled: false 129 | 130 | Style/EmptyLiteral: 131 | Description: 'Prefer literals to Array.new/Hash.new/String.new.' 132 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#literal-array-hash' 133 | Enabled: true 134 | 135 | Style/EmptyLinesAroundBlockBody: 136 | Description: "Keeps track of empty lines around block bodies." 137 | Enabled: false 138 | 139 | Style/EmptyLinesAroundClassBody: 140 | Description: "Keeps track of empty lines around class bodies." 141 | Enabled: false 142 | 143 | Style/EmptyLinesAroundModuleBody: 144 | Description: "Keeps track of empty lines around module bodies." 145 | Enabled: false 146 | 147 | Style/EmptyLinesAroundMethodBody: 148 | Description: "Keeps track of empty lines around method bodies." 149 | Enabled: false 150 | 151 | # Checks whether the source file has a utf-8 encoding comment or not 152 | # AutoCorrectEncodingComment must match the regex 153 | # /#.*coding\s?[:=]\s?(?:UTF|utf)-8/ 154 | Style/Encoding: 155 | Enabled: false 156 | 157 | Style/EvenOdd: 158 | Description: 'Favor the use of Fixnum#even? && Fixnum#odd?' 159 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' 160 | Enabled: false 161 | 162 | Style/ExtraSpacing: 163 | Description: 'Do not use unnecessary spacing.' 164 | Enabled: true 165 | 166 | Style/FileName: 167 | Description: 'Use snake_case for source file names.' 168 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files' 169 | Enabled: false 170 | 171 | Style/FrozenStringLiteralComment: 172 | Description: >- 173 | Add the frozen_string_literal comment to the top of files 174 | to help transition from Ruby 2.3.0 to Ruby 3.0. 175 | Enabled: false 176 | 177 | Style/FlipFlop: 178 | Description: 'Checks for flip flops' 179 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-flip-flops' 180 | Enabled: false 181 | 182 | Style/FormatString: 183 | Description: 'Enforce the use of Kernel#sprintf, Kernel#format or String#%.' 184 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#sprintf' 185 | Enabled: false 186 | 187 | Style/GlobalVars: 188 | Description: 'Do not introduce global variables.' 189 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#instance-vars' 190 | Reference: 'http://www.zenspider.com/Languages/Ruby/QuickRef.html' 191 | Enabled: false 192 | 193 | Style/GuardClause: 194 | Description: 'Check for conditionals that can be replaced with guard clauses' 195 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals' 196 | Enabled: false 197 | 198 | Style/IfUnlessModifier: 199 | Description: >- 200 | Favor modifier if/unless usage when you have a 201 | single-line body. 202 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier' 203 | Enabled: false 204 | 205 | Style/IfWithSemicolon: 206 | Description: 'Do not use if x; .... Use the ternary operator instead.' 207 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs' 208 | Enabled: false 209 | 210 | Style/InlineComment: 211 | Description: 'Avoid inline comments.' 212 | Enabled: false 213 | 214 | Style/Lambda: 215 | Description: 'Use the new lambda literal syntax for single-line blocks.' 216 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#lambda-multi-line' 217 | Enabled: false 218 | 219 | Style/LambdaCall: 220 | Description: 'Use lambda.call(...) instead of lambda.(...).' 221 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc-call' 222 | Enabled: false 223 | 224 | Style/LineEndConcatenation: 225 | Description: >- 226 | Use \ instead of + or << to concatenate two string literals at 227 | line end. 228 | Enabled: false 229 | 230 | Metrics/LineLength: 231 | Description: 'Limit lines to 120 characters.' 232 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits' 233 | Max: 120 234 | Enabled: true 235 | 236 | Metrics/MethodLength: 237 | Description: 'Avoid methods longer than 10 lines of code.' 238 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods' 239 | Enabled: false 240 | 241 | Style/ModuleFunction: 242 | Description: 'Checks for usage of `extend self` in modules.' 243 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#module-function' 244 | Enabled: false 245 | 246 | Style/MultilineOperationIndentation: 247 | Description: >- 248 | Checks indentation of binary operations that span more than 249 | one line. 250 | Enabled: true 251 | EnforcedStyle: indented 252 | 253 | Style/MultilineBlockChain: 254 | Description: 'Avoid multi-line chains of blocks.' 255 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks' 256 | Enabled: false 257 | 258 | Style/MultilineMethodCallIndentation: 259 | Description: >- 260 | Checks indentation of method calls with the dot operator 261 | that span more than one line. 262 | Enabled: true 263 | EnforcedStyle: indented 264 | 265 | Style/NegatedIf: 266 | Description: >- 267 | Favor unless over if for negative conditions 268 | (or control flow or). 269 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#unless-for-negatives' 270 | Enabled: false 271 | 272 | Style/NegatedWhile: 273 | Description: 'Favor until over while for negative conditions.' 274 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#until-for-negatives' 275 | Enabled: false 276 | 277 | Style/Next: 278 | Description: 'Use `next` to skip iteration instead of a condition at the end.' 279 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals' 280 | Enabled: false 281 | 282 | Style/NilComparison: 283 | Description: 'Prefer x.nil? to x == nil.' 284 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' 285 | Enabled: false 286 | 287 | Style/Not: 288 | Description: 'Use ! instead of not.' 289 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bang-not-not' 290 | Enabled: false 291 | 292 | Style/NumericLiterals: 293 | Description: >- 294 | Add underscores to large numeric literals to improve their 295 | readability. 296 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics' 297 | Enabled: false 298 | 299 | Style/NumericPredicate: 300 | Description: >- 301 | This cop checks for usage of comparison operators 302 | (`==`, `!=`, `>`, `<`) to test numbers as 303 | zero, nonzero, positive, or negative. 304 | These can be replaced by their respective predicate methods. 305 | The cop can also be configured to do the reverse. 306 | StyleGuide: 'http://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Style/NumericPredicate' 307 | Enabled: false 308 | 309 | Style/OneLineConditional: 310 | Description: >- 311 | Favor the ternary operator(?:) over 312 | if/then/else/end constructs. 313 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#ternary-operator' 314 | Enabled: false 315 | 316 | Style/OpMethod: 317 | Description: 'When defining binary operators, name the argument other.' 318 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg' 319 | Enabled: false 320 | 321 | Metrics/ParameterLists: 322 | Description: 'Avoid parameter lists longer than three or four parameters.' 323 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#too-many-params' 324 | Enabled: false 325 | 326 | Style/PercentLiteralDelimiters: 327 | Description: 'Use `%`-literal delimiters consistently' 328 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-literal-braces' 329 | Enabled: false 330 | 331 | Style/PerlBackrefs: 332 | Description: 'Avoid Perl-style regex back references.' 333 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers' 334 | Enabled: false 335 | 336 | Style/PredicateName: 337 | Description: 'Check the names of predicate methods.' 338 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark' 339 | NamePrefixBlacklist: 340 | - is_ 341 | Exclude: 342 | - spec/**/* 343 | 344 | Style/Proc: 345 | Description: 'Use proc instead of Proc.new.' 346 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc' 347 | Enabled: false 348 | 349 | Style/RaiseArgs: 350 | Description: 'Checks the arguments passed to raise/fail.' 351 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#exception-class-messages' 352 | Enabled: false 353 | 354 | Style/RegexpLiteral: 355 | Description: 'Use / or %r around regular expressions.' 356 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-r' 357 | Enabled: false 358 | 359 | Style/SelfAssignment: 360 | Description: >- 361 | Checks for places where self-assignment shorthand should have 362 | been used. 363 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#self-assignment' 364 | Enabled: false 365 | 366 | Style/SingleLineBlockParams: 367 | Description: 'Enforces the names of some block params.' 368 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#reduce-blocks' 369 | Enabled: false 370 | 371 | Style/SingleLineMethods: 372 | Description: 'Avoid single-line methods.' 373 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-single-line-methods' 374 | Enabled: false 375 | 376 | Style/SignalException: 377 | Description: 'Checks for proper usage of fail and raise.' 378 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#fail-method' 379 | Enabled: false 380 | 381 | Style/SpecialGlobalVars: 382 | Description: 'Avoid Perl-style global variables.' 383 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms' 384 | Enabled: false 385 | 386 | Style/StringLiterals: 387 | Description: 'Checks if uses of quotes match the configured preference.' 388 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-string-literals' 389 | EnforcedStyle: double_quotes 390 | Enabled: true 391 | 392 | Style/TrailingCommaInArguments: 393 | Description: 'Checks for trailing comma in argument lists.' 394 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas' 395 | EnforcedStyleForMultiline: 396 | - consistent_comma 397 | - no_comma 398 | SupportedStyles: 399 | - comma 400 | - consistent_comma 401 | - no_comma 402 | Enabled: true 403 | 404 | Style/TrailingCommaInLiteral: 405 | Description: 'Checks for trailing comma in array and hash literals.' 406 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas' 407 | EnforcedStyleForMultiline: 408 | - consistent_comma 409 | - no_comma 410 | SupportedStyles: 411 | - comma 412 | - consistent_comma 413 | - no_comma 414 | Enabled: true 415 | 416 | Style/TrivialAccessors: 417 | Description: 'Prefer attr_* methods to trivial readers/writers.' 418 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr_family' 419 | Enabled: false 420 | 421 | Style/VariableInterpolation: 422 | Description: >- 423 | Don't interpolate global, instance and class variables 424 | directly in strings. 425 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#curlies-interpolate' 426 | Enabled: false 427 | 428 | Style/WhenThen: 429 | Description: 'Use when x then ... for one-line cases.' 430 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#one-line-cases' 431 | Enabled: false 432 | 433 | Style/WhileUntilModifier: 434 | Description: >- 435 | Favor modifier while/until usage when you have a 436 | single-line body. 437 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier' 438 | Enabled: false 439 | 440 | Style/WordArray: 441 | Description: 'Use %w or %W for arrays of words.' 442 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-w' 443 | Enabled: false 444 | 445 | # Lint 446 | 447 | Lint/AmbiguousOperator: 448 | Description: >- 449 | Checks for ambiguous operators in the first argument of a 450 | method invocation without parentheses. 451 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-as-args' 452 | Enabled: false 453 | 454 | Lint/AmbiguousRegexpLiteral: 455 | Description: >- 456 | Checks for ambiguous regexp literals in the first argument of 457 | a method invocation without parenthesis. 458 | Enabled: false 459 | 460 | Lint/AssignmentInCondition: 461 | Description: "Don't use assignment in conditions." 462 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition' 463 | Enabled: false 464 | 465 | Lint/CircularArgumentReference: 466 | Description: "Don't refer to the keyword argument in the default value." 467 | Enabled: false 468 | 469 | Lint/ConditionPosition: 470 | Description: >- 471 | Checks for condition placed in a confusing position relative to 472 | the keyword. 473 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#same-line-condition' 474 | Enabled: false 475 | 476 | Lint/DeprecatedClassMethods: 477 | Description: 'Check for deprecated class method calls.' 478 | Enabled: false 479 | 480 | Lint/DuplicatedKey: 481 | Description: 'Check for duplicate keys in hash literals.' 482 | Enabled: false 483 | 484 | Lint/EachWithObjectArgument: 485 | Description: 'Check for immutable argument given to each_with_object.' 486 | Enabled: false 487 | 488 | Lint/ElseLayout: 489 | Description: 'Check for odd code arrangement in an else block.' 490 | Enabled: false 491 | 492 | Lint/FormatParameterMismatch: 493 | Description: 'The number of parameters to format/sprint must match the fields.' 494 | Enabled: false 495 | 496 | Lint/HandleExceptions: 497 | Description: "Don't suppress exception." 498 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions' 499 | Enabled: false 500 | 501 | Lint/InvalidCharacterLiteral: 502 | Description: >- 503 | Checks for invalid character literals with a non-escaped 504 | whitespace character. 505 | Enabled: false 506 | 507 | Style/InitialIndentation: 508 | Description: >- 509 | Checks the indentation of the first non-blank non-comment line in a file. 510 | Enabled: false 511 | 512 | Lint/LiteralInCondition: 513 | Description: 'Checks of literals used in conditions.' 514 | Enabled: false 515 | 516 | Lint/LiteralInInterpolation: 517 | Description: 'Checks for literals used in interpolation.' 518 | Enabled: false 519 | 520 | Lint/Loop: 521 | Description: >- 522 | Use Kernel#loop with break rather than begin/end/until or 523 | begin/end/while for post-loop tests. 524 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#loop-with-break' 525 | Enabled: false 526 | 527 | Lint/NestedMethodDefinition: 528 | Description: 'Do not use nested method definitions.' 529 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-methods' 530 | Enabled: false 531 | 532 | Lint/NonLocalExitFromIterator: 533 | Description: 'Do not use return in iterator to cause non-local exit.' 534 | Enabled: false 535 | 536 | Lint/ParenthesesAsGroupedExpression: 537 | Description: >- 538 | Checks for method calls with a space before the opening 539 | parenthesis. 540 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' 541 | Enabled: false 542 | 543 | Lint/RequireParentheses: 544 | Description: >- 545 | Use parentheses in the method call to avoid confusion 546 | about precedence. 547 | Enabled: false 548 | 549 | Lint/UnderscorePrefixedVariableName: 550 | Description: 'Do not use prefix `_` for a variable that is used.' 551 | Enabled: false 552 | 553 | Lint/UnneededDisable: 554 | Description: >- 555 | Checks for rubocop:disable comments that can be removed. 556 | Note: this cop is not disabled when disabling all cops. 557 | It must be explicitly disabled. 558 | Enabled: false 559 | 560 | Lint/Void: 561 | Description: 'Possible use of operator/literal/variable in void context.' 562 | Enabled: false 563 | 564 | # Performance 565 | 566 | Performance/CaseWhenSplat: 567 | Description: >- 568 | Place `when` conditions that use splat at the end 569 | of the list of `when` branches. 570 | Enabled: false 571 | 572 | Performance/Count: 573 | Description: >- 574 | Use `count` instead of `select...size`, `reject...size`, 575 | `select...count`, `reject...count`, `select...length`, 576 | and `reject...length`. 577 | Enabled: false 578 | 579 | Performance/Detect: 580 | Description: >- 581 | Use `detect` instead of `select.first`, `find_all.first`, 582 | `select.last`, and `find_all.last`. 583 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code' 584 | Enabled: false 585 | 586 | Performance/FlatMap: 587 | Description: >- 588 | Use `Enumerable#flat_map` 589 | instead of `Enumerable#map...Array#flatten(1)` 590 | or `Enumberable#collect..Array#flatten(1)` 591 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code' 592 | Enabled: false 593 | 594 | Performance/ReverseEach: 595 | Description: 'Use `reverse_each` instead of `reverse.each`.' 596 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code' 597 | Enabled: false 598 | 599 | Performance/Sample: 600 | Description: >- 601 | Use `sample` instead of `shuffle.first`, 602 | `shuffle.last`, and `shuffle[Fixnum]`. 603 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#arrayshufflefirst-vs-arraysample-code' 604 | Enabled: false 605 | 606 | Performance/Size: 607 | Description: >- 608 | Use `size` instead of `count` for counting 609 | the number of elements in `Array` and `Hash`. 610 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#arraycount-vs-arraysize-code' 611 | Enabled: false 612 | 613 | Performance/StringReplacement: 614 | Description: >- 615 | Use `tr` instead of `gsub` when you are replacing the same 616 | number of characters. Use `delete` instead of `gsub` when 617 | you are deleting characters. 618 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code' 619 | Enabled: false 620 | 621 | # Rails 622 | 623 | Rails/ActionFilter: 624 | Description: 'Enforces consistent use of action filter methods.' 625 | Enabled: false 626 | 627 | Rails/Date: 628 | Description: >- 629 | Checks the correct usage of date aware methods, 630 | such as Date.today, Date.current etc. 631 | Enabled: false 632 | 633 | Rails/FindBy: 634 | Description: 'Prefer find_by over where.first.' 635 | Enabled: false 636 | 637 | Rails/FindEach: 638 | Description: 'Prefer all.find_each over all.find.' 639 | Enabled: false 640 | 641 | Rails/HasAndBelongsToMany: 642 | Description: 'Prefer has_many :through to has_and_belongs_to_many.' 643 | Enabled: false 644 | 645 | Rails/Output: 646 | Description: 'Checks for calls to puts, print, etc.' 647 | Enabled: false 648 | 649 | Rails/ReadWriteAttribute: 650 | Description: >- 651 | Checks for read_attribute(:attr) and 652 | write_attribute(:attr, val). 653 | Enabled: false 654 | 655 | Rails/ScopeArgs: 656 | Description: 'Checks the arguments of ActiveRecord scopes.' 657 | Enabled: false 658 | 659 | Rails/TimeZone: 660 | Description: 'Checks the correct usage of time zone aware methods.' 661 | StyleGuide: 'https://github.com/bbatsov/rails-style-guide#time' 662 | Reference: 'http://danilenko.org/2012/7/6/rails_timezones' 663 | Enabled: false 664 | 665 | Rails/Validation: 666 | Description: 'Use validates :attribute, hash of validations.' 667 | Enabled: false 668 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | omniauthcanvas -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.4.1 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | sudo: required 3 | dist: trusty 4 | 5 | env: 6 | - CXX=g++-4.8 7 | 8 | addons: 9 | postgresql: "9.5" 10 | apt: 11 | sources: 12 | - ubuntu-toolchain-r-test 13 | packages: 14 | - g++-4.8 15 | 16 | cache: 17 | bundler: true 18 | 19 | branches: 20 | only: 21 | - master 22 | - staging 23 | - stable 24 | 25 | services: 26 | - postgresql 27 | 28 | install: 29 | - bundle install 30 | 31 | script: 32 | - rspec 33 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # clean up: 2 | # rm -rf ~/.bundle/ ~/.gem/; rm -rf $GEM_HOME/bundler/ $GEM_HOME/cache/bundler/; rm -rf .bundle/; rm -rf vendor/cache/; rm -rf Gemfile.lock 3 | 4 | source "http://rubygems.org" 5 | 6 | gemspec 7 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | guard 'rspec', :version => 2 do 2 | watch(%r{^spec/.+_spec\.rb$}) 3 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } 4 | watch('spec/spec_helper.rb') { "spec" } 5 | end 6 | 7 | 8 | guard 'bundler' do 9 | watch('Gemfile') 10 | watch(/^.+\.gemspec/) 11 | end 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OmniAuth Canvas [![Build Status](https://travis-ci.org/atomicjolt/omniauth-canvas.svg?branch=master)](https://travis-ci.org/atomicjolt/omniauth-canvas) 2 | Gem to authenticate with Instructure Canvas via OAuth2 3 | 4 | # Background 5 | OmniAuth Canvas grew out of the need to simplify the process of setting up LTI 6 | and connecting a user account on to Instructure Canvas on various projects built by 7 | [Atomic Jolt](http://www.atomicjolt.com). 8 | 9 | # Example 10 | [Atomic Jolt](http://www.atomicjolt.com) has created an LTI started application that demonstrates 11 | how to use the OAuth gem: 12 | [LTI Starter App](https://github.com/atomicjolt/lti_starter_app) 13 | 14 | # Setup 15 | Contact Instructure or your Canvas administrator to get an OAuth key and secret. 16 | By default omniauth-canvas will attempt to authenticate with http://canvas.instructure.com. 17 | 18 | **NOTE**: you will need to set `env['rack.session']['oauth_site']` to the current 19 | Canvas instance that you wish to OAuth with. By default this is https://canvas.instructure.com 20 | 21 | -- OR -- 22 | 23 | to dynamically set the canvas site url do one of the following. 24 | 25 | ## Standard setup 26 | 27 | ```ruby 28 | use OmniAuth::Builder do 29 | provider :canvas, 'canvas_key', 'canvas_secret', :setup => lambda{|env| 30 | request = Rack::Request.new(env) 31 | env['omniauth.strategy'].options[:client_options].site = env['rack.session']['oauth_site'] 32 | } 33 | end 34 | ``` 35 | 36 | ## Setup with Devise 37 | 38 | ```ruby 39 | config.omniauth :canvas, 'canvas_key', 'canvas_secret', :setup => lambda{|env| 40 | request = Rack::Request.new(env) 41 | env['omniauth.strategy'].options[:client_options].site = env['rack.session']['oauth_site'] 42 | } 43 | ``` 44 | 45 | ## Alternative Setup 46 | 47 | In this setup, you do not have to set `env['rack.session']['oauth_site']` 48 | 49 | ```ruby 50 | Rails.application.config.middleware.use OmniAuth::Builder do 51 | provider :canvas, APP_CONFIG['canvas_client_id'], APP_CONFIG['canvas_client_secret'], 52 | { 53 | :client_options => { 54 | :site => APP_CONFIG['canvas_host'] 55 | } 56 | } 57 | end 58 | ``` 59 | 60 | 61 | # Canvas Configuration 62 | 63 | If you are running your own installation of canvas, you can generate a client ID 64 | and secret in the Site Admin account of your Canvas install. There will be a 65 | "Developer Keys" tab on the left navigation sidebar. For more information, 66 | consult the [Canvas OAuth Documentation](https://canvas.instructure.com/doc/api/file.oauth.html) 67 | 68 | 69 | # State 70 | 71 | In most cases your application will need to restore state after handling the OAuth process 72 | with Canvas. Since many applications that integrate with Canvas will be launched via the LTI 73 | protocol inside of an iframe sessions may not be available. To restore application state the 74 | omniauth-canvas gem uses the "state" parameter provided by the LTI proctocol. You will need 75 | to add the following code to your application to take advantage of this functionality: 76 | 77 | 78 | Add the following initializer in `config/initializers/omniauth.rb`: 79 | 80 | ```ruby 81 | OmniAuth.config.before_request_phase do |env| 82 | request = Rack::Request.new(env) 83 | state = "#{SecureRandom.hex(24)}#{DateTime.now.to_i}" 84 | OauthState.create!(state: state, payload: request.params.to_json) 85 | env["omniauth.strategy"].options[:authorize_params].state = state 86 | 87 | # Bye default omniauth will store all params in the session. The code above 88 | # stores the values in the database so we remove the values from the session 89 | # since the amount of data in the original params object will overflow the 90 | # allowed cookie size 91 | env["rack.session"].delete("omniauth.params") 92 | end 93 | ``` 94 | 95 | Add the following middleware to `lib/middlware/oauth_state_middleware.rb`: 96 | 97 | ```ruby 98 | class OauthStateMiddleware 99 | def initialize(app) 100 | @app = app 101 | end 102 | 103 | def call(env) 104 | request = Rack::Request.new(env) 105 | if request.params["state"] && request.params["code"] 106 | if oauth_state = OauthState.find_by(state: request.params["state"]) 107 | # Restore the param from before the OAuth dance 108 | state_params = JSON.parse(oauth_state.payload) || {} 109 | state_params.each do |key, value| 110 | request.update_param(key, value) 111 | end 112 | application_instance = ApplicationInstance.find_by(lti_key: state_params["oauth_consumer_key"]) 113 | env["canvas.url"] = application_instance.lti_consumer_uri 114 | oauth_state.destroy 115 | else 116 | raise OauthStateMiddlewareException, "Invalid state in OAuth callback" 117 | end 118 | end 119 | @app.call(env) 120 | end 121 | end 122 | 123 | class OauthStateMiddlewareException < RuntimeError 124 | end 125 | ``` 126 | 127 | This middleware relies upon two models - OauthState and ApplicationInstance. OauthState is used to 128 | store relevant state before sending the user to Canvas to finish the OAuth. ApplicationInstance is 129 | model used in Atomic Jolt projects that is used to store the Canvas Url so that it can be reset in 130 | the environment. You don't need to implement the same model, but you will need to store the user's 131 | Canvas URL somewhere before sending the user to OAuth with Canvas. Change the following lines in the 132 | above code to recover the Canvas URL from where ever it is stored: 133 | 134 | ``` 135 | application_instance = ApplicationInstance.find_by(lti_key: state_params["oauth_consumer_key"]) 136 | env["canvas.url"] = application_instance.lti_consumer_uri 137 | ``` 138 | 139 | The OauthState model looks like this: 140 | ``` 141 | class OauthState < ActiveRecord::Base 142 | validates :state, presence: true, uniqueness: true 143 | end 144 | ``` 145 | 146 | With the following schema: 147 | ``` 148 | create_table "oauth_states", force: :cascade do |t| 149 | t.string "state" 150 | t.text "payload" 151 | t.datetime "created_at", null: false 152 | t.datetime "updated_at", null: false 153 | t.index ["state"], name: "index_oauth_states_on_state", using: :btree 154 | end 155 | ``` 156 | 157 | Last, enable the middleware by adding the following to `config/application.rb`: 158 | 159 | ```ruby 160 | # Middleware that can restore state after an OAuth request 161 | config.middleware.insert_before 0, "OauthStateMiddleware" 162 | ``` 163 | 164 | 165 | # License 166 | 167 | Copyright (C) 2012-2017 by Justin Ball and Atomic Jolt. 168 | 169 | Permission is hereby granted, free of charge, to any person obtaining a copy 170 | of this software and associated documentation files (the "Software"), to deal 171 | in the Software without restriction, including without limitation the rights 172 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 173 | copies of the Software, and to permit persons to whom the Software is 174 | furnished to do so, subject to the following conditions: 175 | 176 | The above copyright notice and this permission notice shall be included in 177 | all copies or substantial portions of the Software. 178 | 179 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 180 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 181 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 182 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 183 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 184 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 185 | THE SOFTWARE. 186 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | $: << File.dirname(__FILE__) 3 | 4 | require 'bundler' 5 | require 'rake' 6 | require 'rspec/core/rake_task' 7 | 8 | Bundler::GemHelper.install_tasks 9 | 10 | task :default => [:spec] 11 | task :test => [:spec] 12 | 13 | desc "run spec tests" 14 | RSpec::Core::RakeTask.new('spec') do |t| 15 | t.pattern = 'spec/**/*_spec.rb' 16 | end 17 | -------------------------------------------------------------------------------- /lib/omniauth-canvas.rb: -------------------------------------------------------------------------------- 1 | require "omniauth-canvas/version" 2 | require "omniauth/strategies/canvas" 3 | -------------------------------------------------------------------------------- /lib/omniauth-canvas/version.rb: -------------------------------------------------------------------------------- 1 | module OmniAuth 2 | module Canvas 3 | VERSION = "2.0.0".freeze 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/omniauth/strategies/canvas.rb: -------------------------------------------------------------------------------- 1 | require "omniauth-oauth2" 2 | 3 | module OmniAuth 4 | module Strategies 5 | class Canvas < OmniAuth::Strategies::OAuth2 6 | 7 | option :name, "canvas" 8 | 9 | option :client_options, 10 | site: "https://canvas.instructure.com", 11 | authorize_url: "/login/oauth2/auth", 12 | token_url: "/login/oauth2/token" 13 | 14 | # Canvas does use state but we want to control it rather than letting 15 | # omniauth-oauth2 handle it. 16 | option :provider_ignores_state, true 17 | 18 | option :token_params, 19 | parse: :json 20 | 21 | uid do 22 | access_token["user"]["id"] 23 | end 24 | 25 | info do 26 | { 27 | "name" => raw_info["name"], 28 | "email" => raw_info["primary_email"], 29 | "bio" => raw_info["bio"], 30 | "title" => raw_info["title"], 31 | "nickname" => raw_info["login_id"], 32 | "active_avatar" => raw_info["avatar_url"], 33 | "url" => access_token.client.site 34 | } 35 | end 36 | 37 | extra do 38 | { raw_info: raw_info } 39 | end 40 | 41 | def raw_info 42 | @raw_info ||= access_token.get("/api/v1/users/#{access_token['user']['id']}/profile").parsed 43 | end 44 | 45 | # Passing any query string value to Canvas will result in: 46 | # redirect_uri does not match client settings 47 | # so we set the value to empty string 48 | def query_string 49 | "" 50 | end 51 | 52 | # Override authorize_params so that we can be deliberate about the value for state 53 | # and not use the session which is unavailable inside of an iframe for some 54 | # browsers (ie Safari) 55 | def authorize_params 56 | # Only set state if it hasn't already been set 57 | options.authorize_params[:state] ||= SecureRandom.hex(24) 58 | params = options.authorize_params.merge(options_for("authorize")) 59 | if OmniAuth.config.test_mode 60 | @env ||= {} 61 | @env["rack.session"] ||= {} 62 | end 63 | params 64 | end 65 | 66 | end 67 | end 68 | end 69 | 70 | OmniAuth.config.add_camelization "canvas", "Canvas" 71 | -------------------------------------------------------------------------------- /omniauth-canvas.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | 4 | # Maintain your gem's version: 5 | require "omniauth-canvas/version" 6 | 7 | Gem::Specification.new do |gem| 8 | gem.name = "omniauth-canvas" 9 | gem.version = OmniAuth::Canvas::VERSION 10 | gem.authors = "Justin Ball" 11 | gem.email = "justin.ball@atomicjolt.com" 12 | gem.description = "OmniAuth Oauth2 strategy for Instructure Canvas." 13 | gem.summary = "OmniAuth Oauth2 strategy for Instructure Canvas." 14 | gem.homepage = "https://github.com/atomicjolt/omniauth-canvas" 15 | gem.license = "MIT" 16 | 17 | gem.required_ruby_version = ">= 2.5" 18 | 19 | gem.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"] 20 | gem.test_files = Dir["test/**/*"] 21 | 22 | gem.require_paths = ["lib"] 23 | 24 | gem.add_dependency "omniauth", "~> 2.0" 25 | gem.add_dependency "omniauth-oauth2", "~> 1.8" 26 | 27 | gem.add_development_dependency "rake" 28 | gem.add_development_dependency "rack-test" 29 | gem.add_development_dependency "rspec" 30 | gem.add_development_dependency "byebug" 31 | end 32 | -------------------------------------------------------------------------------- /spec/omniauth/strategies/canvas_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe OmniAuth::Strategies::OAuth2 do 4 | def app; lambda { |_env| [200, {}, ["Hello."]] } end 5 | 6 | before do 7 | @request = double("Request") 8 | allow(@request).to receive(:params).and_return({}) 9 | OmniAuth.config.test_mode = true 10 | end 11 | 12 | subject do 13 | OmniAuth::Strategies::Canvas.new(nil, @options || {}).tap do |strategy| 14 | allow(strategy).to receive(:request).and_return(@request) 15 | end 16 | end 17 | 18 | after do 19 | OmniAuth.config.test_mode = false 20 | end 21 | 22 | context "client options" do 23 | it "has correct api site" do 24 | expect(subject.options.client_options.site).to eq("https://canvas.instructure.com") 25 | end 26 | 27 | it "has correct access token path" do 28 | expect(subject.options.client_options.token_url).to eq("/login/oauth2/token") 29 | end 30 | 31 | it "has correct authorize url" do 32 | expect(subject.options.client_options.authorize_url).to eq("/login/oauth2/auth") 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $:.unshift File.expand_path("..", __FILE__) 2 | $:.unshift File.expand_path("../../lib", __FILE__) 3 | 4 | require "rspec" 5 | require "rack/test" 6 | require "omniauth" 7 | require "omniauth-canvas" 8 | 9 | RSpec.configure do |config| 10 | config.include Rack::Test::Methods 11 | config.extend OmniAuth::Test::StrategyMacros, type: :strategy 12 | end 13 | --------------------------------------------------------------------------------