samdjstevens / java-totp Goto Github PK
View Code? Open in Web Editor NEWA java library for implementing Time-based One Time Passwords for Multi-Factor Authentication.
License: MIT License
A java library for implementing Time-based One Time Passwords for Multi-Factor Authentication.
License: MIT License
Howdy!
I understand that I'm kind of abusing the issue system by using it to ask this question, however I am at my wits end with this and need to be pointed in the right direction. Maybe you can help.
I'm using your Library to implement 2FA in an Android app, alongside Firebase Auth as the primary authentication method. When I start up my application, checking if the user is currently logged in is quite easy. However, I can't figure out how I might save the fact that the user has passed the TOTP check since they logged in.
This is my first time trying to implement user authentication in a project if that wasn't immediately obvious. I've been trying to come up with my own solution to this for a few days, but I'm at the point where I need to ask for help, and Stack Overflow hasn't been able to supply any.
Your project's pom.xml declare a dependency to ZXing version 3.3.0 (whose javase module uses JAI Image IO 1.3.1)
ZXing updates are available :
Is there any rational behind your choice that we should be aware of ?
Hi,
I'm using this library to generate and validate totp values. I generate a new code every 60 seconds,
long currentBucket = Math.floorDiv(new SystemTimeProvider().getTime(), 60);
System.out.println( codeGenerator.generate(secret, currentBucket) );`
However, when I validate the codes, the old codes continue to be valid even after the new codes are generated. Is this the normal case or am I doing something wrong?
timeProvider = new SystemTimeProvider();
codeGenerator = new DefaultCodeGenerator(HashingAlgorithm.SHA1);
verifier = new DefaultCodeVerifier(codeGenerator, timeProvider);
verifier.setTimePeriod(60);
verifier.setAllowedTimePeriodDiscrepancy(5);
verifier.isValidCode(secret, totp);
thank you
Cx8fd408ac-dd80 8.1 Inclusion of Functionality from Untrusted Control Sphere vulnerability pending CVSS allocation
I Search About Error:
https://devhub.checkmarx.com/cve-details/Cx8fd408ac-dd80/?utm_source=jetbrains&utm_medium=referral&utm_campaign=idea
Hi
Sorry for asking here if it's not the place but I tried stackoverflow with no response.
I'm trying to validate a token generated by an App (FortiToken Mobile if it matters) but the validator keeps returning false to me.
I went over the usage section in the home page here several times but I can't see what I missed.
Here is the code I play with - It has the qr code generation, validation, and trying to get a token so I can email the user for him to enter it back.
Any help/direction would be great
Thanks
`
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.getWriter().append("Served at: ").append(request.getContextPath());
//SecretGenerator secretGenerator = new DefaultSecretGenerator();
//String secret = secretGenerator.generate();
String secret = "XAFXRG3TNMLHENVAQTD5ZJOTC2MHTIVE";
// ========================================
// QR code generation section
// ========================================
QrData data = new QrData.Builder()
.label("[email protected]")
.secret(secret)
.issuer("PORTAL")
.algorithm(HashingAlgorithm.SHA256) // More on this below
.digits(6)
.period(60)
.build();
try {
QrGenerator generator = new ZxingPngQrGenerator();
byte[] imageData = generator.generate(data);
String mimeType = generator.getImageMimeType();
String dataUri = getDataUriForImage(imageData, mimeType);
response.getWriter().append("\r\ndataUri: " + dataUri);//.append(request.getContextPath());
} catch (QrGenerationException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
}
CodeGenerator codeGenerator = new DefaultCodeGenerator(HashingAlgorithm.SHA256);
// ========================================
// Generating a code to email the user
// ========================================
try {
String codeToCheck = codeGenerator.generate(secret, 1); // what should come here instead of the "1"
// codeToCheck is never the ok - I guess because of the second parameter which I don't know what to send.
response.getWriter().append("\r\ncodeToCheck: " + codeToCheck);//.append(request.getContextPath());
} catch (CodeGenerationException ex){
ex.printStackTrace();
}
// ========================================
// Getting code from the url and checking it
// ========================================
String code = request.getQueryString().replace("code=", "");
response.getWriter().append("\r\nCode: " + code);//.append(request.getContextPath());
TimeProvider timeProvider = new SystemTimeProvider();
DefaultCodeVerifier verifier = new DefaultCodeVerifier(codeGenerator, timeProvider);
verifier.setTimePeriod(60);
verifier.setAllowedTimePeriodDiscrepancy(2);
// secret = the shared secret for the user
// code = the code submitted by the user
boolean successful = verifier.isValidCode(secret, code);
if (successful) System.out.println(successful);
response.getWriter().append("\r\nResult: " + successful);//.append(request.getContextPath());
}
`
Because Auto Config is not working. So we need manually import the auto config class.
@ImportAutoConfiguration(TotpAutoConfiguration.class)
Some values that should be configurable are currently hardcoded and cannot be changed. Allow these values to be changed by consumers to make the library more flexible.
The values that should be configurable are:
This a request for enhancement regarding the build process and the indication provided in default github README page :
Resources :
https://github.com/codecov/example-java
Example of other TOTP java project with this codecoverage metrics :
https://github.com/j256/two-factor-auth
It would be a great addition to allow users to create a different colored QR codes - this is especially useful if you plan to show the code in dark mode for instance and want to invert the colors.
From looking at the code, it seems the used MatrixToImageWriter
supports passing in a custom MatrixToImageConfig
object, which defines the ON and OFF colors used to create the image.
See https://zxing.github.io/zxing/apidocs/com/google/zxing/client/j2se/MatrixToImageWriter.html#writeToStream-com.google.zxing.common.BitMatrix-java.lang.String-java.io.OutputStream-com.google.zxing.client.j2se.MatrixToImageConfig- and https://zxing.github.io/zxing/apidocs/com/google/zxing/client/j2se/MatrixToImageConfig.html
Add some warnings in the README about using SHA256 and SHA512, with a list of known apps that do and don't support them.
Hi,
I am trying to implement your library for very simple use.
Only code generation from secret and then checking it.
But this verification always fail.
I use the SystemTimeProvider and all defaults parameters.
Could you help me with it ?
Cordially.
Base32 is currently being used to generate recovery code. Therefore, the alphabet that makes up the generation of the recovery code is 32 characters (from A to Z and from 2 to 7). As the key size is 10 characters, the total number of keys possible in this alphabet is is 32^10.
The calculation of the entropy "x" is calculated as follows: x = log(32^10)/log(2) = 50
The entropy of current code is 50 bits.
Because recovery codes MUST be kept simple to read and enter by end user, it is important to keep a simple character set, without being very long.
Suggestion for alternative recovery code implementation :
The entropy using those settings would be log(36^16)/log(2) = 82.7
, matching security requirements.
This would generate recovery code like "phuo-1dm4-647k-8i2n"
. This same format is for example being used by DropBox for its MFA.
It could be argued that 0 and 1 should be removed from the character set to prevent confusion with O and L/I when read/entered. However this would reduce the entropy to 81, unless length is increased to 17 or more. I think with a proper font this is not required.
(Incoming pull request)
The sample code in the documentation indicates the following usage :
CodeGenerator codeGenerator = new DefaultCodeGenerator();
Problem : CodeGenerator interface is package protected, thus it cannot be used.
(Obvious) Workaround :
DefaultCodeGenerator codeGenerator = new DefaultCodeGenerator();
Hi,
I have been using your library for handling TOTP based QR code generation and validation from last 1 year. It was working successfully with google and microsoft authenticator. Just all of a sudden the TOTP validation is failing which I am failing to understand why?
Below is the code to generate QR code which i scan using authenticator app:
QrData data = new QrData.Builder().label(userDispVal).secret(secret).issuer(label).algorithm(HashingAlgorithm.SHA1).digits(6).period(30).build();
QrGenerator generator = new ZxingPngQrGenerator();
byte[] imageData = generator.generate(data);
String mimeType = generator.getImageMimeType();
String dataUri = getDataUriForImage(imageData, mimeType);
and below is the code to validate the the TOTP code generated by authenticator app:
TimeProvider timeProvider = new SystemTimeProvider();
CodeGenerator codeGenerator = new DefaultCodeGenerator(HashingAlgorithm.SHA1);
DefaultCodeVerifier verifier = new DefaultCodeVerifier(codeGenerator, timeProvider);
boolean successful = verifier.isValidCode(secret, code);
The last call is always returning false. It was working earlier like I mentioned.
Any help in this regard will be appreciated.
Thanks,
Ashutosh
Code:-
public byte[] generateQrCode(Users user) throws QrGenerationException {
SecretGenerator secretGenerator = new DefaultSecretGenerator();
String secret = secretGenerator.generate();
QrData data = new QrData.Builder().label(user.getEmail()).secret(secret).issuer("ABC")
.algorithm(HashingAlgorithm.SHA1) // More on this below
.digits(6).period(30).build();
QrGenerator generator = new ZxingPngQrGenerator();
return generator.generate(data);
}
Error:-
5:52:24.618 [http-nio-8090-exec-2] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler processing failed; nested exception is java.lang.ExceptionInInitializerError] with root cause
java.lang.NullPointerException: null
at javax.imageio.spi.IIORegistry.getDefaultInstance(IIORegistry.java:156)
at javax.imageio.ImageIO.(ImageIO.java:66)
at com.google.zxing.client.j2se.MatrixToImageWriter.writeToStream(MatrixToImageWriter.java:159)
at com.google.zxing.client.j2se.MatrixToImageWriter.writeToStream(MatrixToImageWriter.java:144)
at dev.samstevens.totp.qr.ZxingPngQrGenerator.generate(ZxingPngQrGenerator.java:41)
at com.pfy.newservice.AuthenticatorService.generateQrCode(AuthenticatorService.java:30)
at com.pfy.newservice.AuthenticatorService$$FastClassBySpringCGLIB$$c99c7e83.invoke()
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
at com.pfy.newservice.AuthenticatorService$$EnhancerBySpringCGLIB$$b8d82602.generateQrCode()
at com.pfy.rest.AuthenticatorResource.generateQrCode(AuthenticatorResource.java:46)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:832)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:743)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:961)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:869)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at com.pfy.CORSFilter.doFilter(CORSFilter.java:35)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:87)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1502)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1458)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Hi & Thanks for this lib. We use the generator & verifier to generate simple expiring OTPs (not looking at the full MFA usecase).
One difficulty we had, was the use of the generator, especially what to pass for the counter parameter. It is easy to figure out that it should be relying on the TimeProvider, but in order of our usecase to work, we need to pass
Math.floorDiv(timeProvider.getTime(), timePeriodInSeconds)
and i guess we're not alone there. This is hard to figure out until you read the code of the verifier.
I was wondering if documentation can be improved here, or (maybe even better) the API can be augmented to be more usable.
For instance, why not have a
public String generate(String key, DefaultCodeVerifier counter) throws CodeGenerationException
so that OTPs can be generated according to the parameters of the verifier (which is weird, because we're not using the interface CodeVerifier here. Maybe extending this with the necessary parameters would help.
Another way to do this would be to have a
public String generate(String key, TimeProvider tprov, int timePeriod) throws CodeGenerationException
But as people look at the method with the min number of params first, some javadoc would really help nevertheless.
What do you think?
PS: i'd be open to create a PR if you want me to.
For a secure TOTP I need a method to check the validity of 3 TOTP codes.
In my example I check 24 hours ahead and behind. The 3 codes must exist in this period
and they must be consecutive.
The method must accept the secret and 3 TOTP codes and returns two results.
Here an idea how this could look like:
TimeProvider timeProvider = new SystemTimeProvider();
CodeGenerator codeGenerator = new DefaultCodeGenerator();
DefaultCodeVerifier verifier = new DefaultCodeVerifier(codeGenerator, timeProvider);
long periods = TimeUnit.HOURS.toSeconds(24) / 30;
//would be nice if I could use verifier.getTimePeriod(), instead of the hardcoded 30
//or there is a method to set the discrepancy in seconds and the library calculates the periods
verifier.setAllowedTimePeriodDiscrepancy((int)periods);
// setAllowedTimePeriodDiscrepancy does not accept long
VerificationResult result = verifier.areCodesValid(secret, code1, code2, code3);
//result should contain a boolean, if the codes are valid and consecutive
//and it should return a time shift number (int). How many periods the codes are behind or ahead
For my example TOTP application I had to write my own TOTP verifier based on the now archived aerogear project. Would be nice if I could switch to your library.
See also this issue for more details why this method is needed.
CodeVerifier works all fine for me with the default values for HashingAlgorithm = SHA1, and time period validity = 30.
With anyhing different, however, CodeVerifier is failing to verify codes for me.
For my testing i use both - your CodeGenerator.generate() and user-side MFA mobile apps (eg, RedHat FreeOTP 1.5, Duo Mobile 3.30).
As long as your CodeGenerator will still validate its codes with SHA256 or SHA512 with time period 30, when it comes to a period validity any other than 30 it seems to always fail.
When I use the above mentioned mobile apps they seem to work ONLY with the default SHA1 and time period validity of 30. Anything different fails for me.
I do use the same Hashing and Period validity values when creating QRCode and then CodeGenerator/CodeVerifier for verification.
Thank you for this nice library!
I happen to run my app in a few devices that don't have properly sync'd clocks, which caused TOTP codes to fail.
It seemed pretty easy, just using NtpTimeProvider should do it!
Unfortunately it didn't work:
Turns out that NtpTimeProvider currently uses timeInfo.getReturnTime()
which is the the local timestamp for the reply -- No NTP data is used at all!
The fixed code looks like this (Maybe ntpOffset could be cached?)
@Override
public long getTime() throws TimeProviderException {
long ntpOffset = 0;
try {
TimeInfo timeInfo = client.getTime(ntpHost);
timeInfo.computeDetails();
if (timeInfo.getOffset() != null) {
ntpOffset = timeInfo.getOffset();
Log.i(TAG, "NTP offset to calculate TOTP is " + ntpOffset + "ms");
} else {
Log.w(TAG, "Unable to calculate NTP offset");
}
} catch (Exception e) {
Log.w(TAG, "Failed to get NTP offset", e);
}
return (System.currentTimeMillis() + ntpOffset) / 1000;
}
Hello and thank you very much for this library, it works like a charm! :)
I have a question though: I would like to display an authenticator configuration key below the QR Code for mobile users (it's easier for them to copy paste the key as they can't scan the QR Code with the same device they are browsing the app from), but I use SHA 256 for the QR Code generation and code verification. So:
Is there something special to implement to have an authenticator configuration key that matches the QR Code?
Thanks a lot in advance :D
(Sorry if this is not the place to discuss such matters)
Were you able to successfully use hashing algorithm other than SHA-1 with any application ?
I could not...
I have tested the following application and I could not make it work :
On Android :
On Windows :
Do you known any application supporting hashing algorithm other than SHA-1 ?
Is this library (still) suitable for production use today? It seems not to have been updated in a rather long time for a security-oriented package.
Hi,
that qr code we generate work fine in android google auth but doesn't work in ios.
can you help me ?
When will a version supporting Springboot3 be released?
In the current state of java-totp, the commons-net
is only used for NtpTimeProvider
feature.
My suggestion would be to declare commons-net
as an optional dependency, and update the documentation, to indicate that use of NtpTimeProvider
requires an additional explicit declaration of commons-net
.
This would reduce the default transitive dependencies to the minimum required.
Resources :
https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html
Workaround for user of you library that wish to reduce their transitive dependencies : exclude commons-net
from their pom :
<dependency>
<groupId>dev.samstevens.totp</groupId>
<artifactId>totp</artifactId>
<exclusions>
<!-- Exclude commons-net, it is used by samsstevens-topt for Ntp implementation, we don't use this -->
<exclusion>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
</exclusion>
<!-- Exclude jcommander, it is used by xzing in commandline, we don't use this -->
<exclusion>
<groupId>com.beust</groupId>
<artifactId>jcommander</artifactId>
</exclusion>
</exclusions>
</dependency>
PS : thanks for providing this nice and simple library. ๐๐ป๐๐ป
Hi,
This is a great package! Amazing.
I just have one question.
How can I confirm whether the QR code has been scanned?
In case that the user didn't scan the QR, but the server side has registered the secret key, then both side(server side and the MFA APP) couldn't sync the key info. Then the MFA login would fail.
How to make the server side notified that the QR has been scanned and the key has registered on the MFA APP?
Many thanks.
TotpAutoConfiguration
is not being loaded automatically by Spring Boot 3
.
As a result, all beans defined in TotpAutoConfiguration
, such as SecretGenerator
, QrDataFactory
, QrGenerator
, CodeVerifier
, are not recognized.
I had to import TotpAutoConfiguration
manually in my Spring Boot 3
application:
package com.acme.config;
import dev.samstevens.totp.spring.autoconfigure.TotpAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(TotpAutoConfiguration.class)
public class TotpConfiguration {
}
Apply Guideline 4-5 / EXTEND-5 from the Secure Coding Guidelines for Java SE
Design classes and methods for inheritance or declare them final [6]. Left non-final, a class or method can be maliciously overridden by an attacker. A class that does not permit subclassing is easier to implement and verify that it is secure. Prefer composition to inheritance.
When running mvn test on Java 11 the following test will fail :
[INFO] Running dev.samstevens.totp.util.DataUriEncodingTest
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.032 s <<< FAILURE! - in dev.samstevens.totp.util.DataUriEncodingTest
[ERROR] testDataUriEncode(dev.samstevens.totp.util.DataUriEncodingTest) Time elapsed: 0.03 s <<< FAILURE!
org.junit.ComparisonFailure: expected:<...AAAFeAQAAAADlUEq3AAA[C30lEQVR42u2bPZLiQAyFRREQ7hHmKBzNPpqPwhEcElDWtt5T2267ganaZIPnYIaxPwiEfp96zH9/vUywYMGCBf8jvBgu96f9TINdn38eNlxeN3/cx/IXHl4Fd+F45eNiN/fJQNlQHpndx6W8tSKCO/Az7vsr4GGpli1W97EYv1jdBH+El3wc78GjcNjlKvg77PTNi/scQW4WQW6CP8OMbtr5Ak9FmrS7v00Fgnc1Bd7IVNj86hYgwW0nA5OGi65v/dD2CK52Lk1LucJFy51HxDoSYwn5iPWL4D5Mk0YqLEGOOhyPeRNJ0wX3YeRAXOGihlQ4wsBOc7f+LNhbO6Py1o65VuVoAr3jooK3clxuxKvZwlM5rtHqoKJUC+7D7FY4tdWMyPzIcjwI7sPlJzKi3WBnpkkYPw082S4VCG7gLLk54hYDY9iYI9bjE2wX3YKt6WRufIzEiCaw/Hrl1HZqAgW35RiqXnjjwE7GjJ+AuffYyQje2XmmJhWygK3xHL4ZzzJNCj7DsDM1KbNqbmoFFBDyOxDcg6lCPbfSwhluGrKmCO7DcaOuLqyOuI7EaBze2o5RcE+hop0d+gomXWov42l9JnjrZNgLpjrAeJ4humCG2/fPgo8uaquyzJnD1gnkpD8LbkaPKaJ7TqfM3oVL27DzYU4RvEnKoRVUOY/LH9bo3OQKfgMv+Rgrx7qmpUJFqW/fyQg+wfeR5yo4w2Fqq93fw+xgZ8HN0jaDnCtHqwpV54CK4AZel4wlI25L2x+vQtVBjRd86p/jfs69WVpSoRL8DoawwnI8pv5uFBC4lTwcixJ82HdHfozyQWkqPTW/A8F9eD+11YaGezOv2vzFBXdhzhxVJKAab1Dj18gX/AamjmdoAjPI62G8PIFxFfwFRiCjkzHLbhpjySL4G+xbOcbB4xx/R3fBfbieY+S2FnNaHhx4lwoENzWF49p62AIXqINWIFj/gCNYsGDB/zH8F1f/M45si6u5]AAAAAElFTkSuQmCC> but was:<...AAAFeAQAAAADlUEq3AAA[DD0lEQVR4Xu2ZPXYqMQxGlZMiZZbAUljazNJYyiyBkoIzerqfDDHYcJL6SQWD7WuKb/RnY/57u9rzzBsruLeCeyu4t4J7+z/g3WTuFzucFvu8fG+2fFy/fDuuMdLiZ8FTmG++7vblfjJRtsSS2XHdY+sNKXgCX5j3K/CiEcqG6r6G+KG6FfwW5sEye7SEw+5y2ILfw1CEtfuZIDcjyDPWC34Jx4ffdCa6I8hJk3bkh56ju+AONhnzW6bCh4cWC57Cd5OkuOh9a79acL/a6RxNSxguGjMbsa7EGCFPrDNZ8AROSYMNndX2aTknlTQRv+AJrBwow0WpIq6qfD7wQPy0ggdYOqvy4qlt9EFVDjiL89NLKfgOM8G3s+GpeVxL1UX5+lC7C+7g7FYsW5iWEV35cUd8Wwqew/FJPIfA0hkXPab4TWC9g4KncCu5eWoL23TYOBPr/IJiveAJjG9qWYmRjMjj2k5tYGkFDzDlOHTWPA9GpvxoGA7bvZSCOzjdkDupb5bv8YxvsnbINFnwCEtnDcJucqszzJpyewcFz2AEzqq8ZmlZ2Xjgco+tKX7BA8xE5MD7NXw6pRKj5eFt1Z6CRxhNe52j+/N0UR5MOlbwAOfZlgHlI2sK8UwTKJ0f++eCn13UNOHyRpzSFN1MNaTgEUZgPxHd5+aUW/YusSdrdO4peIRddwWZA3XHp2VqdPsnt+AX8N6W+fOHHIiyxgnk4MQ676ArxwU/w5zTVExQViNFN1RsdazgEf7MyqsgN3zTcNFbdBv2o3PBjzA6tz3I3fYcWFNVZmvBMzgkVf/MfFYRYNJkHoaXgl/BtH0GbBHWWlh1eIv2Bv886vcKHuFm5EfKB4nR2IqntndQ8ByWlvTPVN7W0Kz+U463x8u9gjsYET3/pj0tfN+MKkI33XQu+BV8iQjOVtlpaAhyz04mo3t9rt0FD7ACOUamIEf8PJZ00V3wHPafcswF89GzKrOn4CnM13XPHMjDFeQvorvgsabgopIUKuc49w53BQXf4d9awb0V3FvBvRXcW8G9/Q3+B1f/M451MqUo]AAAAAElFTkSuQmCC>
at dev.samstevens.totp.util.DataUriEncodingTest.testDataUriEncode(DataUriEncodingTest.java:18)
Explanation :
When reading/write an image, the behavior may (will!) different from one Java version to another.
This is explained here :
https://stackoverflow.com/questions/46790556/java-9-imageio-read-write-gives-a-different-result-than-java-8
Suggested fix (Pull request incoming) : Do not use any image API in this test.
Rational : The purpose of the method Utils.getDataUriForImage being tested is not to read or write an image by following some image format specification. But simply to ensure that a specified byte array (representing an image) gets written using the Data URI specification in base64.
Fix : Rewrite the test just to check that :
Dear,
It would be great if user can specify the border size of the generated QR.
Good library ;)
It would be interesting to have a timeprovider that can be instantiated by passing a clock instance as a parameter.
This would be useful for testing and when working with instances of Clock
private class ClockTimeProvider implements TimeProvider {
private Clock clock;
public ClockTimeProvider(Clock clock) {
this.clock = clock;
}
@Override
public long getTime() throws TimeProviderException {
return Instant.now(this.clock).getEpochSecond();
}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.