Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1Password Support for Database Connections #422

Merged
merged 4 commits into from
May 8, 2017
Merged

Conversation

cocojoe
Copy link
Member

@cocojoe cocojoe commented Apr 5, 2017

All PasswordPolicy levels supported in Sign Up password generation
Added Lock options showPasswordManager, passwordManagerAppIdentifier
Embedded OnePassword ObjC Extension into Lock

@cocojoe
Copy link
Member Author

cocojoe commented Apr 5, 2017

onepassword_login
onepassword_signup

@cocojoe
Copy link
Member Author

cocojoe commented Apr 5, 2017

Test Notes:

Must have 1Password app installed on device.

info.plist

Add following to the app's info.plist:

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>org-appextension-feature-password-management</string>
</array>

Options

By default the bundler identifier will be used, If you want to share credentials with a website that uses Auth0, then use the name of the site e.g.

.withOptions {
    $0.passwordManagerAppIdentifier = "www.website.com"
}

@cocojoe cocojoe force-pushed the added-1password-support branch from 5723f1b to 1379367 Compare April 5, 2017 14:00
@cocojoe
Copy link
Member Author

cocojoe commented Apr 6, 2017

Additional thoughts, currently the password recipe will match the DB password policy, although this feels incredibly weak for anything < fair. Given that if you are using a password manager you are mostly setting it and forgetting it. Perhaps the default should be fair+ ?

@cocojoe cocojoe requested a review from hzalaz April 6, 2017 13:20
Copy link
Member

@hzalaz hzalaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the 1password button is a bit bigger than I'd like

@@ -0,0 +1,210 @@
//
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd keep the name without the Lock prefix and also I hope this is no part of the public headers right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it's not, they recommend renaming it so I did that 'just in case'. I made it an isolated commit so easy to revert.

https://github.com/auth0/Lock.swift/blob/added-1password-support/Lock/LockOnePasswordExtension.h#L57

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably makes sense for apps to avoid clashing

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But here we don't need to rename it since it won't be a public header

@@ -90,6 +90,9 @@ public protocol OptionBuildable: Options {

/// Specify the passwordless method, send a passcode or send a magic link. By default is .emailCode
var passwordlessMethod: PasswordlessMethod { get set }

/// Enable and specify the 1Password identifier to use, typically you will want to use the app's bundle identifier. However you may have a website and want to ensure 1Password works with both by using an identifier like `www.mysite.com`
var enableOnePasswordWithIdentifier: String? { get set }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's split this in two

      /// Show password manager button when user credentials are needed, e.g. during Sign Up, and allow storing/retrieving credentials from the password manager
var showPasswordManager: Bool { get set }

     /// Application identifier used by the password manager, if you also have a web app we recommend adding your site url. By default is the application bundle identifier
var passwordManagerAppIdentifier: String { get set }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I merged it into one, as I look at it from the perspective of if I am going to enable it then I will want to use it, so why not in streamline into one option? However I have no strong feelings either way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First I'd like it to be enabled by default and ideally some people won't set the app identifier and prefer to use the bundle identifier

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought perhaps passwordManagerAppIdentifier should be optional as Bundle.main.bundleIdentifier is an optional. Although the user has bigger issues in their app if their bundleIdentifier is nil. So forced it.

We should probably add another option for the Password Title in 1Password, by default it will parse the identifier however it's designed to parse website urls so looks weird.

We could of course just pass the CFBundleDisplayName on the store method as it would also be strange not to use the same name as the app for 1Password.

image uploaded from ios

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've set it to use display name as title for now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can have passwordManagerDisplayName(Default bundle display name) or improve the options and have a struct to configure the password manager with the three properties inside

return [ AppExtensionGeneratedPasswordMinLengthKey: "8",
AppExtensionGeneratedPasswordRequireDigitsKey: true,
AppExtensionGeneratedPasswordRequireSymbolsKey: true ]
case Auth0.excellent.rawValue:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a comment and yes use excellent for every rule even if there is no rule

@hzalaz hzalaz added this to the v2-Next milestone Apr 6, 2017
@cocojoe cocojoe force-pushed the added-1password-support branch 2 times, most recently from e4348a2 to d3468ad Compare April 7, 2017 13:25
@cocojoe cocojoe force-pushed the added-1password-support branch from 373a933 to c3ef57e Compare April 18, 2017 15:26
Copy link
Member

@hzalaz hzalaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the 1pass icon seems too big compared to the field. I'd try to leave around 3-4 pt of padding in the edges

@@ -71,3 +71,19 @@ class OnePassword: PasswordManager {
}
}
}

public struct PasswordManagerConfig {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't use the same PasswordManager instead of creating yet another type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not see any requirement or need for PasswordManager Protocol to be a Public API. It also utilises internal types such as InputField...
It is debatable to use a struct vs properties for 2-3 values anyway. So can always revert to properties.
Although another type is not a big deal either.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use the OnePassword type and configure it, the protocol can still be private (and implemented by OnePassword) since we are not allowing any extra extensibility.


/// Application identifier used by the password manager, if you also have a web app we recommend adding your site url. By default is the application bundle identifier
var passwordManagerAppIdentifier: String { get set }
/// Configuration to be used by the password manager.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we explain a bit better what it means in the code comment?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also we need to explain its attributes

@@ -71,3 +71,19 @@ class OnePassword: PasswordManager {
}
}
}

public struct PasswordManagerConfig {
public var isEnabled: Bool = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think enabled is fine

README.md Outdated

```swift
.withOptions {
$0.passwordManager = PasswordManagerConfig(appIdentifier: "www.myapp.com", displayName: "My App")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did originally then changed it, will revert.

@@ -52,6 +52,10 @@ struct ClassicRouter: Router {
let authentication = self.lock.authentication
let interactor = DatabaseInteractor(connection: database, authentication: authentication, user: self.user, options: self.lock.options, dispatcher: lock.observerStore)
let presenter = DatabasePresenter(interactor: interactor, connection: database, navigator: self, options: self.lock.options)
if self.lock.options.passwordManager.isEnabled, OnePassword.isAvailable() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Below I suggested to join the config with the object itself so we could just ask it if its enabled (user settings + app availability in the same query).

Ideally we could just assign it all the time and ask it if its enabled when trying to use it since we will do the same but with the optional

Copy link
Member Author

@cocojoe cocojoe Apr 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure there is any real life advantage, if OnePassword is not available on device (still needs checked), there is not much point in initialising the manager just to perform a bool check vs an optional check.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Less code, encapsulation and simplicity. The presenter will always have a password manager and will always ask it if it should display the button. All the thing is done in the presenter and the router only needs to inject it.

@@ -0,0 +1,210 @@
//
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But here we don't need to rename it since it won't be a public header

}
}

public struct PasswordManagerConfig {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just merge the password manager and the config its not necessary to add an extra type

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also let's add code comments to the attributes

}

public struct PasswordManagerConfig {
public var isEnabled: Bool = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets use enabled

@@ -90,6 +90,9 @@ public protocol OptionBuildable: Options {

/// Specify the passwordless method, send a passcode or magic link. By default is .code
var passwordlessMethod: PasswordlessMethod { get set }

/// Configuration to be used by the password manager.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets explain it a bit more

@cocojoe cocojoe force-pushed the added-1password-support branch 2 times, most recently from 0753496 to b4234ad Compare April 20, 2017 10:03
Copy link
Member

@hzalaz hzalaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cocojoe also please update the screenshots showing the button, if needed, so I can see how it looks now

@@ -108,9 +109,15 @@ class DatabaseOnlyView: UIView, DatabaseView {
self.layoutSecondaryButton(self.allowedModes.contains(.ResetPassword))
self.form = form

if passwordManager.enabled, passwordManager.isAvailable() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably I wasn't clear before but ideally the passwordManager.isAvaliable() could be encapsulated inside the passwordManager.enabled

@@ -85,7 +86,7 @@ class DatabaseOnlyView: UIView, DatabaseView {
private let separatorIndex = 2
private let socialIndex = 1

func showLogin(withIdentifierStyle style: DatabaseIdentifierStyle, identifier: String? = nil, authCollectionView: AuthCollectionView? = nil) {
func showLogin(withIdentifierStyle style: DatabaseIdentifierStyle, identifier: String? = nil, authCollectionView: AuthCollectionView? = nil, passwordManager: PasswordManager) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a design stand, passing the password manager is sort of breaching the separation of concerns of the view and presenter. Ideally the view should only know that it should show the button (just a simple true or false) then the rest of the work should be in the presenter (the wiring)

public var displayName: String

weak var controller: UIViewController?
var fields: [String: InputField] = [:]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why I didnt spot this before but I am not too convinced about this mapping. AFAIK the dict will strong retain the field and thats a thing we want to avoid. Also mixing something from the view here is sort of a code smell, maybe we need to work this around by either having closures but maybe that is not enough either


public init() {
self.appIdentifier = Bundle.main.bundleIdentifier!
self.displayName = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as! String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No force-wrap please

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather use here a default like <No App>. This wont be ever execute since Xcode will fail without the bundle name but its better than a force-wrap

@@ -119,6 +119,36 @@ class PasswordPolicySpec: QuickSpec {

}

describe("one password extension") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some way we can test this in a better way?

@cocojoe
Copy link
Member Author

cocojoe commented Apr 25, 2017

simulator screen shot 25 apr 2017 13 20 27

Will add styling to this once I can rebase style branch (WIP)

@cocojoe cocojoe force-pushed the added-1password-support branch from 7ac54e9 to 4243a0b Compare April 25, 2017 13:32
@@ -146,6 +149,23 @@ class DatabasePresenter: Presentable, Loggable {
view.secondaryButton?.onPress = { button in
self.navigator.navigate(.forgotPassword)
}

if let identifyField = view.identityField, let passwordField = view.passwordField {
passwordManager.onUpdate = { identifier, password in
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good but in the end we end up having the same retain behavior with a different implementation

button.color = color
container.addSubview(button)

self.textFieldRightPadding?.constant = -50
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Being picky but can't we disable this constraint and add a new one between field and button?


public init() {
self.appIdentifier = Bundle.main.bundleIdentifier.verbatim()
self.displayName = Bundle.main.object(forInfoDictionaryKey: "CFBundleName").verbatim()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOL, nice trick

@cocojoe cocojoe force-pushed the added-1password-support branch 2 times, most recently from f9f42b4 to f8ce528 Compare April 27, 2017 15:11
@cocojoe
Copy link
Member Author

cocojoe commented May 2, 2017

@hzalaz this can be merged

}
}
view.passwordManagerButton?.onPress = { _ in
self.passwordManager.login {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are retaining the presenter here

}
}
view.passwordManagerButton?.onPress = { _ in
self.passwordManager.store(withPolicy: passwordPolicyValidator?.policy.onePasswordRules(), identifier: self.creator.identifier) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are retaining the presenter here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For both, there is no strong reference to the block which is why I didn't modify in the last pass. Quick checked in DatabasePresenter.swift with:

deinit {
    print("dealloc")
}

@cocojoe
Copy link
Member Author

cocojoe commented May 3, 2017

@hzalaz I can't merge as the codecov is a bit weird, I set the 3rd Party OnePasswordExtension .h/.m to be ignored in CodeCov

@cocojoe cocojoe force-pushed the added-1password-support branch from f8ce528 to 127c358 Compare May 8, 2017 11:04
cocojoe added 2 commits May 8, 2017 13:54
Added PasswordManager Protocol
Added 1Password Swift Facade
Added IconButton component
OnePassword Assets
Update PodSpec
PasswordManager Added to Classic Router with Extension check
PasswordPolicy extension to convert existing policy into 1Password password Recipe
PasswordManager added to Database Signup, will store username when available
@cocojoe cocojoe force-pushed the added-1password-support branch from 127c358 to 5eb27eb Compare May 8, 2017 13:40
Update README PaswordManager Section
Update Mocks
Tidied up UITests as got broke in rebase (Snapshot)
@cocojoe cocojoe force-pushed the added-1password-support branch from 5eb27eb to 8f862f5 Compare May 8, 2017 14:24
@cocojoe cocojoe merged commit cb0a12d into master May 8, 2017
@hzalaz hzalaz deleted the added-1password-support branch May 8, 2017 23:55
@cocojoe cocojoe mentioned this pull request Jun 6, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants